/**
 * ***************************************************************************** Copyright (c) 2006
 * Subclipse project and others. All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0 which accompanies this
 * distribution, and is available at http://www.eclipse.org/legal/epl-v10.html
 *
 * <p>Contributors: Subclipse project committers - initial API and implementation
 * ****************************************************************************
 */
package org.tigris.subversion.subclipse.ui.history;

import java.io.File;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.resource.JFaceColors;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextOperationTarget;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.TextViewer;
import org.eclipse.jface.text.hyperlink.IHyperlink;
import org.eclipse.jface.text.hyperlink.IHyperlinkDetector;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.text.source.SourceViewer;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.StructuredViewer;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.jface.window.Window;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.program.Program;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.team.core.RepositoryProvider;
import org.eclipse.team.core.TeamException;
import org.eclipse.team.core.variants.IResourceVariant;
import org.eclipse.team.ui.history.HistoryPage;
import org.eclipse.team.ui.history.IHistoryPageSite;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.ContainerSelectionDialog;
import org.eclipse.ui.editors.text.EditorsUI;
import org.eclipse.ui.editors.text.TextSourceViewerConfiguration;
import org.eclipse.ui.part.IPageSite;
import org.eclipse.ui.texteditor.ITextEditorActionConstants;
import org.tigris.subversion.subclipse.core.IResourceStateChangeListener;
import org.tigris.subversion.subclipse.core.ISVNLocalFile;
import org.tigris.subversion.subclipse.core.ISVNLocalResource;
import org.tigris.subversion.subclipse.core.ISVNRemoteFile;
import org.tigris.subversion.subclipse.core.ISVNRemoteResource;
import org.tigris.subversion.subclipse.core.ISVNResource;
import org.tigris.subversion.subclipse.core.SVNException;
import org.tigris.subversion.subclipse.core.SVNProviderPlugin;
import org.tigris.subversion.subclipse.core.SVNStatus;
import org.tigris.subversion.subclipse.core.SVNTeamProvider;
import org.tigris.subversion.subclipse.core.commands.ChangeCommitPropertiesCommand;
import org.tigris.subversion.subclipse.core.commands.GetLogsCommand;
import org.tigris.subversion.subclipse.core.history.AliasManager;
import org.tigris.subversion.subclipse.core.history.ILogEntry;
import org.tigris.subversion.subclipse.core.history.LogEntry;
import org.tigris.subversion.subclipse.core.history.LogEntryChangePath;
import org.tigris.subversion.subclipse.core.resources.LocalResourceStatus;
import org.tigris.subversion.subclipse.core.resources.RemoteFile;
import org.tigris.subversion.subclipse.core.resources.RemoteFolder;
import org.tigris.subversion.subclipse.core.resources.RemoteResource;
import org.tigris.subversion.subclipse.core.resources.SVNWorkspaceRoot;
import org.tigris.subversion.subclipse.ui.CancelableSVNLogMessageCallback;
import org.tigris.subversion.subclipse.ui.IHelpContextIds;
import org.tigris.subversion.subclipse.ui.ISVNUIConstants;
import org.tigris.subversion.subclipse.ui.Policy;
import org.tigris.subversion.subclipse.ui.SVNUIPlugin;
import org.tigris.subversion.subclipse.ui.actions.CompareRemoteResourcesAction;
import org.tigris.subversion.subclipse.ui.actions.ExportRemoteFolderAction;
import org.tigris.subversion.subclipse.ui.actions.GenerateChangeLogAction;
import org.tigris.subversion.subclipse.ui.actions.OpenRemoteFileAction;
import org.tigris.subversion.subclipse.ui.actions.ShowAnnotationAction;
import org.tigris.subversion.subclipse.ui.actions.ShowDifferencesAsUnifiedDiffAction;
import org.tigris.subversion.subclipse.ui.actions.ShowHistoryAction;
import org.tigris.subversion.subclipse.ui.actions.WorkspaceAction;
import org.tigris.subversion.subclipse.ui.console.TextViewerAction;
import org.tigris.subversion.subclipse.ui.dialogs.HistorySearchDialog;
import org.tigris.subversion.subclipse.ui.dialogs.SetCommitPropertiesDialog;
import org.tigris.subversion.subclipse.ui.dialogs.ShowRevisionsDialog;
import org.tigris.subversion.subclipse.ui.internal.Utils;
import org.tigris.subversion.subclipse.ui.operations.BranchTagOperation;
import org.tigris.subversion.subclipse.ui.operations.MergeOperation;
import org.tigris.subversion.subclipse.ui.operations.ReplaceOperation;
import org.tigris.subversion.subclipse.ui.operations.SwitchOperation;
import org.tigris.subversion.subclipse.ui.settings.ProjectProperties;
import org.tigris.subversion.subclipse.ui.svnproperties.SvnRevPropertiesView;
import org.tigris.subversion.subclipse.ui.util.EmptySearchViewerFilter;
import org.tigris.subversion.subclipse.ui.util.LinkList;
import org.tigris.subversion.subclipse.ui.wizards.BranchTagWizard;
import org.tigris.subversion.subclipse.ui.wizards.ClosableWizardDialog;
import org.tigris.subversion.subclipse.ui.wizards.dialogs.SvnWizard;
import org.tigris.subversion.subclipse.ui.wizards.dialogs.SvnWizardDialog;
import org.tigris.subversion.subclipse.ui.wizards.dialogs.SvnWizardSwitchPage;
import org.tigris.subversion.svnclientadapter.ISVNClientAdapter;
import org.tigris.subversion.svnclientadapter.ISVNProperty;
import org.tigris.subversion.svnclientadapter.SVNRevision;
import org.tigris.subversion.svnclientadapter.SVNRevisionRange;
import org.tigris.subversion.svnclientadapter.SVNUrl;

/**
 * <code>IHistoryPage</code> for generic history view
 *
 * @author Eugene Kuleshov (migration from legacy history view)
 */
public class SVNHistoryPage extends HistoryPage
    implements IResourceStateChangeListener, KeyListener {

  private SashForm svnHistoryPageControl;
  private SashForm innerSashForm;
  private HistorySearchDialog historySearchDialog;

  HistoryTableProvider historyTableProvider;
  TableViewer tableHistoryViewer;
  StructuredViewer changePathsViewer;
  TextViewer textViewer;

  private boolean showComments;
  private boolean showAffectedPaths;
  private boolean wrapCommentsText;
  boolean shutdown = false;

  private ProjectProperties projectProperties;

  private Composite tableParent;

  private static HistorySearchViewerFilter historySearchViewerFilter;

  // cached for efficiency
  ILogEntry[] entries;
  LogEntryChangePath[] currentLogEntryChangePath;
  ILogEntry lastEntry;
  SVNRevision revisionStart = SVNRevision.HEAD;

  AbstractFetchJob fetchLogEntriesJob = null;
  AbstractFetchJob fetchAllLogEntriesJob = null;
  AbstractFetchJob fetchNextLogEntriesJob = null;
  FetchChangePathJob fetchChangePathJob = null;
  AliasManager tagManager;

  IResource resource;
  ISVNRemoteResource remoteResource;
  ISelection selection;

  private SVNRevisionRange[] revisionRanges;
  private boolean revertEnabled;

  private IAction searchAction;
  private IAction clearSearchAction;
  private IAction getNextAction;
  private IAction getAllAction;
  private IAction toggleStopOnCopyAction;
  private IAction toggleIncludeMergedRevisionsAction;
  private IAction toggleCompareToPreviousAction;
  private IAction toggleShowComments;
  private IAction toggleWrapCommentsAction;
  private IAction toggleShowAffectedPathsAction;

  private IAction openAction;
  private IAction compareWithPreviousAction;
  private IAction compareWithPreviousChangedPathAction;
  private IAction getContentsAction;
  private IAction updateToRevisionAction;
  private IAction openChangedPathAction;
  private IAction showHistoryAction;
  private IAction showRevisionPropertiesAction;
  private IAction compareAction;
  private IAction showAnnotationAction;
  private IAction exportAction;
  private IAction createTagFromRevisionChangedPathAction;
  private IAction copyChangedPathAction;

  //  private IAction switchChangedPathAction;
  //  private IAction revertChangesChangedPathAction;

  private IAction createTagFromRevisionAction;
  private IAction setCommitPropertiesAction;
  private IAction showRevisionsAction;
  private IAction revertChangesAction;
  private IAction refreshAction;
  private IAction switchAction;
  private GenerateChangeLogAction generateChangeLogAction;

  private ToggleAffectedPathsOptionAction[] toggleAffectedPathsLayoutActions;
  private ToggleAffectedPathsOptionAction[] toggleAffectedPathsModeActions;

  private TextViewerAction copyAction;
  private TextViewerAction selectAllAction;

  private LinkList linkList;
  private Cursor handCursor;
  private Cursor busyCursor;

  private IPreferenceStore store = SVNUIPlugin.getPlugin().getPreferenceStore();

  private boolean includeTags = true;
  private boolean includeBugs = false;

  public SVNHistoryPage(Object object) {
    SVNProviderPlugin.addResourceStateChangeListener(this);
  }

  public void dispose() {
    super.dispose();

    SVNProviderPlugin.removeResourceStateChangeListener(this);

    if (busyCursor != null) {
      busyCursor.dispose();
    }
    if (handCursor != null) {
      handCursor.dispose();
    }
  }

  public Control getControl() {
    return svnHistoryPageControl;
  }

  public void setFocus() {
    if (tableHistoryViewer != null) {
      tableHistoryViewer.getControl().setFocus();
    }
  }

  public String getDescription() {
    // TODO Auto-generated method stub
    return null;
  }

  public String getName() {
    return remoteResource == null
        ? null
        : remoteResource.getRepositoryRelativePath()
            + Policy.bind("SVNHistoryPage.0") // $NON-NLS-1$
            + remoteResource.getRepository();
  }

  public boolean isValidInput(Object object) {
    if (object instanceof IResource) {
      RepositoryProvider provider =
          RepositoryProvider.getProvider(((IResource) object).getProject());
      return provider instanceof SVNTeamProvider;
    } else if (object instanceof ISVNRemoteResource) {
      return true;
    }

    // TODO
    // } else if(object instanceof CVSFileRevision) {
    // return true;
    // } else if(object instanceof CVSLocalFileRevision) {
    // return true;

    return false;
  }

  public void refresh() {
    entries = null;
    lastEntry = null;
    revisionStart = SVNRevision.HEAD;
    // show a Busy Cursor during refresh
    BusyIndicator.showWhile(
        tableHistoryViewer.getTable().getDisplay(),
        new Runnable() {
          public void run() {
            if (resource != null) {
              try {
                remoteResource = SVNWorkspaceRoot.getBaseResourceFor(resource);
                historyTableProvider.setRemoteResource(remoteResource);
                projectProperties = ProjectProperties.getProjectProperties(resource);
                historyTableProvider.setProjectProperties(projectProperties);
              } catch (SVNException e) {
              }
            }
            if (tableHistoryViewer.getInput() == null) tableHistoryViewer.setInput(remoteResource);
            tableHistoryViewer.refresh();
            tableHistoryViewer.resetFilters();
            Object firstElement = tableHistoryViewer.getElementAt(0);
            if (firstElement != null) {
              tableHistoryViewer.reveal(firstElement);
            }
            getClearSearchAction().setEnabled(false);
          }
        });
  }

  public Object getAdapter(Class adapter) {
    // TODO Auto-generated method stub
    return null;
  }

  public boolean inputSet() {
    Object input = getInput();

    if (input instanceof IResource) {
      IResource res = (IResource) input;

      RepositoryProvider teamProvider =
          RepositoryProvider.getProvider(res.getProject(), SVNProviderPlugin.getTypeId());
      if (teamProvider != null) {
        try {
          ISVNLocalResource localResource = SVNWorkspaceRoot.getSVNResourceFor(res);
          LocalResourceStatus localResourceStatus =
              (localResource != null) ? localResource.getStatus() : null;
          if (localResource != null
              && localResourceStatus.isManaged()
              && (!localResourceStatus.isAdded() || localResourceStatus.isCopied())) {
            this.resource = res;
            this.remoteResource = localResource.getBaseResource();

            this.projectProperties = ProjectProperties.getProjectProperties(res);
            boolean includeBugs = projectProperties != null;
            boolean includeTags = tagsPropertySet(res);
            if (includeTags != this.includeTags || this.includeBugs != includeBugs) {
              this.includeTags = includeTags;
              this.includeBugs = includeBugs;
              refreshTable();
            }
            this.historyTableProvider.setRemoteResource(this.remoteResource);
            this.historyTableProvider.setProjectProperties(this.projectProperties);
            if (historySearchViewerFilter != null) {

              //            	HistorySearchViewerFilter[] filters = { historySearchViewerFilter };
              //            	this.tableHistoryViewer.setFilters(filters);
              this.tableHistoryViewer.resetFilters();
              this.tableHistoryViewer.addFilter(historySearchViewerFilter);

              historySearchDialog = new HistorySearchDialog(getSite().getShell(), remoteResource);
              historySearchDialog.setSearchAll(false);
              historySearchDialog.setStartRevision(historySearchViewerFilter.getStartRevision());
              historySearchDialog.setEndRevision(historySearchViewerFilter.getEndRevision());
              historySearchViewerFilter = null;
              getClearSearchAction().setEnabled(true);
            } else {
              this.tableHistoryViewer.resetFilters();
              getClearSearchAction().setEnabled(false);
            }
            this.tableHistoryViewer.setInput(this.remoteResource);

            // setContentDescription(Policy.bind("HistoryView.titleWithArgument",
            // baseResource.getName())); //$NON-NLS-1$
            // setTitleToolTip(baseResource.getRepositoryRelativePath());
            return true;
          }
        } catch (TeamException e) {
          SVNUIPlugin.openError(getSite().getShell(), null, null, e);
        }
      }

    } else if (input instanceof ISVNRemoteResource) {
      this.resource = null;
      this.remoteResource = (ISVNRemoteResource) input;

      boolean includeTags = tagsPropertySet(remoteResource);
      if (includeTags != this.includeTags) {
        this.includeTags = includeTags;
        refreshTable();
      }

      try {
        this.projectProperties = ProjectProperties.getProjectProperties(this.remoteResource);
      } catch (SVNException e) {
        if (!e.operationInterrupted()) {
          SVNUIPlugin.openError(getSite().getShell(), null, null, e);
        }
      }
      boolean includeBugs = projectProperties != null;
      if (includeTags != this.includeTags || this.includeBugs != includeBugs) {
        this.includeTags = includeTags;
        this.includeBugs = includeBugs;
        refreshTable();
      }
      this.historyTableProvider.setRemoteResource(this.remoteResource);
      this.historyTableProvider.setProjectProperties(this.projectProperties);
      if (historySearchViewerFilter != null) {
        //      HistorySearchViewerFilter[] filters = { historySearchViewerFilter };
        //      this.tableHistoryViewer.setFilters(filters);
        this.tableHistoryViewer.resetFilters();
        this.tableHistoryViewer.addFilter(historySearchViewerFilter);
        historySearchDialog = new HistorySearchDialog(getSite().getShell(), remoteResource);
        historySearchDialog.setSearchAll(false);
        historySearchDialog.setStartRevision(historySearchViewerFilter.getStartRevision());
        historySearchDialog.setEndRevision(historySearchViewerFilter.getEndRevision());
        historySearchViewerFilter = null;
        getClearSearchAction().setEnabled(true);
      } else {
        this.tableHistoryViewer.resetFilters();
        getClearSearchAction().setEnabled(false);
      }
      this.tableHistoryViewer.setInput(this.remoteResource);
      // setContentDescription(Policy.bind("HistoryView.titleWithArgument",
      // remoteResource.getName())); //$NON-NLS-1$
      // setTitleToolTip(remoteResource.getRepositoryRelativePath());
      return true;
    }

    return false;
  }

  private boolean tagsPropertySet(IResource resource) {
    if (resource == null) return false;
    ISVNLocalResource svnResource = SVNWorkspaceRoot.getSVNResourceFor(resource);
    try {
      if (svnResource.isManaged()) {
        ISVNProperty property = null;
        property = svnResource.getSvnProperty("subclipse:tags"); // $NON-NLS-1$
        if (property != null && property.getValue() != null) return true;
      }
    } catch (SVNException e) {
    }

    IResource checkResource = resource;
    while (checkResource.getParent() != null) {
      checkResource = checkResource.getParent();
      if (checkResource.getParent() == null) return false;
      svnResource = SVNWorkspaceRoot.getSVNResourceFor(checkResource);
      try {
        if (svnResource.isManaged()) {
          ISVNProperty property = null;
          property = svnResource.getSvnProperty("subclipse:tags"); // $NON-NLS-1$
          if (property != null && property.getValue() != null) return true;
        }
      } catch (SVNException e) {
      }
    }

    return false;
  }

  private boolean tagsPropertySet(ISVNRemoteResource resource) {
    ISVNClientAdapter client = null;
    try {
      client = SVNProviderPlugin.getPlugin().getSVNClient();
      ISVNProperty property = null;
      SVNProviderPlugin.disableConsoleLogging();
      property = client.propertyGet(resource.getUrl(), "subclipse:tags"); // $NON-NLS-1$
      if (property != null && property.getValue() != null) {
        SVNProviderPlugin.enableConsoleLogging();
        return true;
      }
      ISVNRemoteResource checkResource = resource;
      while (checkResource.getParent() != null) {
        checkResource = checkResource.getParent();
        property = client.propertyGet(checkResource.getUrl(), "subclipse:tags"); // $NON-NLS-1$
        if (property != null && property.getValue() != null) {
          SVNProviderPlugin.enableConsoleLogging();
          return true;
        }
      }
    } catch (Exception e) {
      SVNProviderPlugin.enableConsoleLogging();
    } finally {
      SVNProviderPlugin.getPlugin().getSVNClientManager().returnSVNClient(client);
    }
    return false;
  }

  public void createControl(Composite parent) {
    this.busyCursor = new Cursor(parent.getDisplay(), SWT.CURSOR_WAIT);
    this.handCursor = new Cursor(parent.getDisplay(), SWT.CURSOR_HAND);

    this.showComments = store.getBoolean(ISVNUIConstants.PREF_SHOW_COMMENTS);
    this.wrapCommentsText = store.getBoolean(ISVNUIConstants.PREF_WRAP_COMMENTS);
    this.showAffectedPaths = store.getBoolean(ISVNUIConstants.PREF_SHOW_PATHS);

    this.svnHistoryPageControl = new SashForm(parent, SWT.VERTICAL);
    this.svnHistoryPageControl.setLayoutData(new GridData(GridData.FILL_BOTH));

    this.toggleAffectedPathsModeActions =
        new ToggleAffectedPathsOptionAction[] {
          new ToggleAffectedPathsOptionAction(
              this,
              "HistoryView.affectedPathsTableLayout", //$NON-NLS-1$
              ISVNUIConstants.IMG_AFFECTED_PATHS_TABLE_MODE,
              ISVNUIConstants.PREF_AFFECTED_PATHS_MODE,
              ISVNUIConstants.MODE_FLAT),
          new ToggleAffectedPathsOptionAction(
              this,
              "HistoryView.affectedPathsFlatLayout", //$NON-NLS-1$
              ISVNUIConstants.IMG_AFFECTED_PATHS_FLAT_MODE,
              ISVNUIConstants.PREF_AFFECTED_PATHS_MODE,
              ISVNUIConstants.MODE_FLAT2),
          new ToggleAffectedPathsOptionAction(
              this,
              "HistoryView.affectedPathsCompressedLayout", //$NON-NLS-1$
              ISVNUIConstants.IMG_AFFECTED_PATHS_COMPRESSED_MODE,
              ISVNUIConstants.PREF_AFFECTED_PATHS_MODE,
              ISVNUIConstants.MODE_COMPRESSED),
        };

    this.toggleAffectedPathsLayoutActions =
        new ToggleAffectedPathsOptionAction[] {
          new ToggleAffectedPathsOptionAction(
              this,
              "HistoryView.affectedPathsHorizontalLayout", //$NON-NLS-1$
              ISVNUIConstants.IMG_AFFECTED_PATHS_HORIZONTAL_LAYOUT,
              ISVNUIConstants.PREF_AFFECTED_PATHS_LAYOUT,
              ISVNUIConstants.LAYOUT_HORIZONTAL),
          new ToggleAffectedPathsOptionAction(
              this,
              "HistoryView.affectedPathsVerticalLayout", //$NON-NLS-1$
              ISVNUIConstants.IMG_AFFECTED_PATHS_VERTICAL_LAYOUT,
              ISVNUIConstants.PREF_AFFECTED_PATHS_LAYOUT,
              ISVNUIConstants.LAYOUT_VERTICAL),
        };

    createTableHistory(svnHistoryPageControl);
    createAffectedPathsViewer();
    contributeActions();

    svnHistoryPageControl.setWeights(new int[] {70, 30});

    // set F1 help
    // PlatformUI.getWorkbench().getHelpSystem().setHelp(svnHistoryPageControl,
    // IHelpContextIds.RESOURCE_HISTORY_VIEW);
    // initDragAndDrop();

    // add listener for editor page activation - this is to support editor
    // linking
    // getSite().getPage().addPartListener(partListener);
    // getSite().getPage().addPartListener(partListener2);
  }

  protected void createTableHistory(Composite parent) {
    boolean redraw = false;
    if (tableParent == null) {
      tableParent = new Composite(parent, SWT.NONE);
      GridLayout layout = new GridLayout();
      layout.numColumns = 1;
      layout.marginHeight = 0;
      layout.marginWidth = 0;
      tableParent.setLayout(layout);
      GridData gridData = new GridData(GridData.GRAB_HORIZONTAL | GridData.HORIZONTAL_ALIGN_FILL);
      gridData.horizontalIndent = 0;
      gridData.verticalIndent = 0;
      tableParent.setLayoutData(gridData);
    } else {
      Control[] children = tableParent.getChildren();
      for (int i = 0; i < children.length; i++) {
        children[i].dispose();
      }
      redraw = true;
    }
    this.historyTableProvider =
        new HistoryTableProvider(
            SWT.H_SCROLL | SWT.V_SCROLL | SWT.MULTI | SWT.FULL_SELECTION,
            "SVNHistoryPage"); //$NON-NLS-1$
    this.historyTableProvider.setRemoteResource(remoteResource);
    this.historyTableProvider.setProjectProperties(this.projectProperties);
    historyTableProvider.setIncludeMergeRevisions(
        store.getBoolean(ISVNUIConstants.PREF_INCLUDE_MERGED_REVISIONS));
    historyTableProvider.setIncludeTags(includeTags);
    historyTableProvider.setIncludeBugs(includeBugs);
    this.tableHistoryViewer = historyTableProvider.createTable(tableParent);
    if (redraw) {
      tableParent.layout(true);
      tableParent.redraw();
    }
    this.tableHistoryViewer.getTable().addKeyListener(this);
    // set the content provider for the table
    this.tableHistoryViewer.setContentProvider(
        new IStructuredContentProvider() {

          public Object[] getElements(Object inputElement) {
            // Short-circuit to optimize
            if (entries != null) return entries;

            if (!(inputElement instanceof ISVNRemoteResource)) return null;
            final ISVNRemoteResource remoteResource = (ISVNRemoteResource) inputElement;

            int entriesToFetch = store.getInt(ISVNUIConstants.PREF_LOG_ENTRIES_TO_FETCH);

            // If we are filtering by revision range, override entries to fetch.
            if (historySearchDialog != null && !historySearchDialog.getSearchAllLogs()) {
              if (historySearchDialog.getStartRevision() != null
                  || historySearchDialog.getEndRevision() != null) {
                if (getClearSearchAction().isEnabled()) entriesToFetch = 0;
              }
            }

            if (entriesToFetch > 0) fetchLogEntriesJob = new FetchLogEntriesJob();
            else fetchLogEntriesJob = new FetchAllLogEntriesJob();
            if (fetchLogEntriesJob.getState() != Job.NONE) {
              fetchLogEntriesJob.cancel();
              try {
                fetchLogEntriesJob.join();
              } catch (InterruptedException e) {
                SVNUIPlugin.log(
                    new SVNException(
                        Policy.bind("HistoryView.errorFetchingEntries", remoteResource.getName()),
                        e)); //$NON-NLS-1$
              }
            }
            fetchLogEntriesJob.setRemoteFile(remoteResource);
            IWorkbenchWindow window =
                SVNUIPlugin.getPlugin().getWorkbench().getActiveWorkbenchWindow();
            if (window.getActivePage() != null
                && window.getActivePage().getActivePart() != null
                && window.getActivePage().getActivePart().getSite() != null) {
              Utils.schedule(fetchLogEntriesJob, window.getActivePage().getActivePart().getSite());
            } else {
              Utils.schedule(fetchLogEntriesJob, null);
            }

            return new Object[0];
          }

          public void dispose() {}

          public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
            entries = null;
            lastEntry = null;
            revisionStart = SVNRevision.HEAD;
          }
        });

    // set the selectionchanged listener for the table
    // updates the comments and affected paths when selection changes
    this.tableHistoryViewer.addSelectionChangedListener(
        new ISelectionChangedListener() {
          private ILogEntry currentLogEntry;
          private int currentSelectionSize = -1;

          public void selectionChanged(SelectionChangedEvent event) {
            ISelection selection = event.getSelection();
            ILogEntry logEntry = getLogEntry((IStructuredSelection) selection);
            if (logEntry != currentLogEntry
                || ((IStructuredSelection) selection).size() != currentSelectionSize) {
              this.currentLogEntry = logEntry;
              this.currentSelectionSize = ((IStructuredSelection) selection).size();
              updatePanels(selection);
            }

            SVNHistoryPage.this.selection = selection;
          }
        });

    // Double click open action
    this.tableHistoryViewer
        .getTable()
        .addListener(
            SWT.DefaultSelection,
            new Listener() {
              public void handleEvent(Event e) {
                if (toggleCompareToPreviousAction.isChecked()) {
                  getCompareWithPreviousAction().run();
                } else {
                  getOpenRemoteFileAction().run();
                }
              }
            });

    // Contribute actions to popup menu for the table
    {
      MenuManager menuMgr = new MenuManager();
      Menu menu = menuMgr.createContextMenu(tableHistoryViewer.getTable());
      menuMgr.addMenuListener(
          new IMenuListener() {
            public void menuAboutToShow(IMenuManager menuMgr) {
              fillTableMenu(menuMgr);
            }
          });
      menuMgr.setRemoveAllWhenShown(true);
      tableHistoryViewer.getTable().setMenu(menu);
      getHistoryPageSite().getPart().getSite().registerContextMenu(menuMgr, tableHistoryViewer);
    }
    if (redraw) {
      parent.layout(true);
      parent.redraw();
    }
  }

  public void refreshTable() {
    createTableHistory(svnHistoryPageControl);
  }

  private void fillChangePathsMenu(IMenuManager manager) {
    //
    // Commented out Get Contents, Revert and Switch options until when/if
    // they can be fixed.  Problem is that we need a way to get the local
    // resource from the LogEntryChangePath.
    //
    IStructuredSelection sel = (IStructuredSelection) changePathsViewer.getSelection();
    if (sel.size() == 1) {
      if (sel.getFirstElement() instanceof LogEntryChangePath) {
        //			  manager.add(getGetContentsAction());
      }
      manager.add(getCreateTagFromRevisionChangedPathAction());
    }
    //	  manager.add(getRevertChangesChangedPathAction());
    //	  manager.add(getSwitchChangedPathAction());
    manager.add(new Separator("exportImportGroup")); // $NON-NLS-1$
    if (sel.size() == 1) {
      if (sel.getFirstElement() instanceof LogEntryChangePath) {
        manager.add(getExportAction());
        if (((LogEntryChangePath) sel.getFirstElement()).getAction() == 'D') {
          manager.add(getCopyChangedPathAction());
        }
      }
    }
    manager.add(new Separator("openGroup")); // $NON-NLS-1$
    if (sel.size() == 1) {
      if (sel.getFirstElement() instanceof LogEntryChangePath) {
        manager.add(getShowAnnotationAction());
      }
      manager.add(getCompareAction());
    }
    if (sel.getFirstElement() instanceof LogEntryChangePath) {
      manager.add(getOpenChangedPathAction());
    }
    if (sel.size() == 1) manager.add(getShowHistoryAction());
  }

  private void fillTableMenu(IMenuManager manager) {
    // file actions go first (view file)
    manager.add(new Separator(IWorkbenchActionConstants.GROUP_FILE));
    // Add the "Add to Workspace" action if 1 revision is selected.
    ISelection sel = tableHistoryViewer.getSelection();
    if (!sel.isEmpty()) {
      if (sel instanceof IStructuredSelection) {
        if (((IStructuredSelection) sel).size() == 1) {
          if (resource != null && resource instanceof IFile) {
            manager.add(getGetContentsAction());
            manager.add(getUpdateToRevisionAction());
          }
          //          manager.add(getShowDifferencesAsUnifiedDiffAction());
          // if (resource != null) {
          manager.add(getCreateTagFromRevisionAction());
          // }
          manager.add(getSetCommitPropertiesAction());

          manager.add(getShowRevisionPropertiesAction());

          ILogEntry logEntry = (ILogEntry) ((IStructuredSelection) sel).getFirstElement();
          if (logEntry.getNumberOfChildren() > 0) manager.add(getShowRevisionsAction());
        }
        if (resource != null) {
          manager.add(getRevertChangesAction());
          if (((IStructuredSelection) sel).size() == 1) manager.add(getSwitchAction());
        }

        manager.add(new Separator("exportImportGroup")); // $NON-NLS-1$
        getGenerateChangeLogAction()
            .setEnabled(!store.getBoolean(ISVNUIConstants.PREF_FETCH_CHANGE_PATH_ON_DEMAND));
        manager.add(getGenerateChangeLogAction());
      }
    }
    manager.add(new Separator("additions")); // $NON-NLS-1$
    manager.add(getRefreshAction());
    manager.add(new Separator("additions-end")); // $NON-NLS-1$
  }

  public void createAffectedPathsViewer() {
    int[] weights = null;
    weights = svnHistoryPageControl.getWeights();
    if (innerSashForm != null) {
      innerSashForm.dispose();
    }
    if (changePathsViewer != null) {
      changePathsViewer.getControl().dispose();
    }

    int mode = store.getInt(ISVNUIConstants.PREF_AFFECTED_PATHS_MODE);
    int layout = store.getInt(ISVNUIConstants.PREF_AFFECTED_PATHS_LAYOUT);

    if (layout == ISVNUIConstants.LAYOUT_HORIZONTAL) {
      innerSashForm = new SashForm(svnHistoryPageControl, SWT.HORIZONTAL);
    } else {
      innerSashForm = new SashForm(svnHistoryPageControl, SWT.VERTICAL);
      createText(innerSashForm);
    }

    switch (mode) {
      case ISVNUIConstants.MODE_COMPRESSED:
        changePathsViewer = new ChangePathsTreeViewer(innerSashForm, this);
        break;
      case ISVNUIConstants.MODE_FLAT2:
        changePathsViewer = new ChangePathsFlatViewer(innerSashForm, this);
        break;
      default:
        changePathsViewer = new ChangePathsTableProvider(innerSashForm, this);
        break;
    }

    changePathsViewer.addSelectionChangedListener(
        new ISelectionChangedListener() {
          public void selectionChanged(SelectionChangedEvent event) {
            SVNHistoryPage.this.selection = changePathsViewer.getSelection();
          }
        });

    changePathsViewer
        .getControl()
        .addListener(
            SWT.DefaultSelection,
            new Listener() {
              public void handleEvent(Event e) {
                if (toggleCompareToPreviousAction.isChecked()) {
                  getCompareWithPreviousChangedPathAction().run();
                } else {
                  getOpenChangedPathAction().run();
                }
              }
            });

    // Contribute actions to changed paths pane
    MenuManager menuMgr = new MenuManager();
    Menu menu = menuMgr.createContextMenu(changePathsViewer.getControl());
    menuMgr.addMenuListener(
        new IMenuListener() {
          public void menuAboutToShow(IMenuManager menuMgr) {
            fillChangePathsMenu(menuMgr);
          }
        });
    menuMgr.setRemoveAllWhenShown(true);
    changePathsViewer.getControl().setMenu(menu);

    if (layout == ISVNUIConstants.LAYOUT_HORIZONTAL) {
      createText(innerSashForm);
    }

    setViewerVisibility();

    innerSashForm.layout();
    if (weights != null && weights.length == 2) {
      svnHistoryPageControl.setWeights(weights);
    }
    svnHistoryPageControl.layout();

    updatePanels(tableHistoryViewer.getSelection());
  }

  /** Create the TextViewer for the logEntry comments */
  protected void createText(Composite parent) {
    // this.textViewer = new TextViewer(parent, SWT.H_SCROLL | SWT.V_SCROLL | SWT.MULTI |
    // SWT.READ_ONLY);
    SourceViewer result =
        new SourceViewer(
            parent, null, null, true, SWT.H_SCROLL | SWT.V_SCROLL | SWT.MULTI | SWT.READ_ONLY);
    result.getTextWidget().setIndent(2);
    result.configure(
        new TextSourceViewerConfiguration(EditorsUI.getPreferenceStore()) {
          public Map getHyperlinkDetectorTargets(ISourceViewer sourceViewer) {
            return Collections.singletonMap(
                "org.eclipse.ui.DefaultTextEditor.Subclipse", //$NON-NLS-1$
                new IAdaptable() {
                  public Object getAdapter(Class adapter) {
                    if (adapter == IResource.class && getInput() instanceof IResource) {
                      return getInput();
                    } else if (adapter == ISVNRemoteResource.class
                        && getInput() instanceof ISVNRemoteResource) {
                      return getInput();
                    }
                    return Platform.getAdapterManager().getAdapter(SVNHistoryPage.this, adapter);
                  }
                });
          }

          public int getHyperlinkStateMask(ISourceViewer sourceViewer) {
            return SWT.NONE;
          }

          public IHyperlinkDetector[] getHyperlinkDetectors(ISourceViewer sourceViewer) {
            IHyperlinkDetector[] detectors = super.getHyperlinkDetectors(sourceViewer);
            IHyperlinkDetector[] newDetectors;
            if (detectors == null) {
              newDetectors = new IHyperlinkDetector[1];
            } else {
              newDetectors = new IHyperlinkDetector[detectors.length + 1];
              System.arraycopy(detectors, 0, newDetectors, 0, detectors.length);
            }

            newDetectors[newDetectors.length - 1] =
                new IHyperlinkDetector() {
                  public IHyperlink[] detectHyperlinks(
                      ITextViewer textViewer, IRegion region, boolean canShowMultipleHyperlinks) {
                    if (linkList == null || !linkList.isLinkAt(region.getOffset())) {
                      return null;
                    }

                    final String linkUrl = linkList.getLinkAt(region.getOffset());
                    final int[] linkRange = linkList.getLinkRange(region.getOffset());

                    return new IHyperlink[] {
                      new IHyperlink() {
                        public IRegion getHyperlinkRegion() {
                          return new Region(linkRange[0], linkRange[1]);
                        }

                        public void open() {
                          try {
                            URL url = new URL(linkUrl);
                            PlatformUI.getWorkbench()
                                .getBrowserSupport()
                                .createBrowser("Subclipse")
                                .openURL(url); // $NON-NLS-1$
                          } catch (Exception e1) {
                            Program.launch(linkUrl);
                          }
                        }

                        public String getHyperlinkText() {
                          return null;
                        }

                        public String getTypeLabel() {
                          return null;
                        }
                      }
                    };
                  }
                };

            return newDetectors;
          }
        });

    this.textViewer = result;

    this.textViewer.addSelectionChangedListener(
        new ISelectionChangedListener() {
          public void selectionChanged(SelectionChangedEvent event) {
            copyAction.update();
          }
        });

    Font font =
        PlatformUI.getWorkbench()
            .getThemeManager()
            .getCurrentTheme()
            .getFontRegistry()
            .get(ISVNUIConstants.SVN_COMMENT_FONT);
    if (font != null) {
      this.textViewer.getTextWidget().setFont(font);
    }

    // Create actions for the text editor (copy and select all)
    copyAction = new TextViewerAction(this.textViewer, ITextOperationTarget.COPY);
    copyAction.setText(Policy.bind("HistoryView.copy")); // $NON-NLS-1$

    selectAllAction = new TextViewerAction(this.textViewer, ITextOperationTarget.SELECT_ALL);
    selectAllAction.setText(Policy.bind("HistoryView.selectAll")); // $NON-NLS-1$

    IHistoryPageSite parentSite = getHistoryPageSite();
    IPageSite pageSite = parentSite.getWorkbenchPageSite();
    IActionBars actionBars = pageSite.getActionBars();

    actionBars.setGlobalActionHandler(ITextEditorActionConstants.COPY, copyAction);
    actionBars.setGlobalActionHandler(ITextEditorActionConstants.SELECT_ALL, selectAllAction);
    actionBars.updateActionBars();

    // Contribute actions to popup menu for the comments area
    {
      MenuManager menuMgr = new MenuManager();
      menuMgr.setRemoveAllWhenShown(true);
      menuMgr.addMenuListener(
          new IMenuListener() {
            public void menuAboutToShow(IMenuManager menuMgr) {
              menuMgr.add(copyAction);
              menuMgr.add(selectAllAction);
            }
          });

      StyledText text = this.textViewer.getTextWidget();
      Menu menu = menuMgr.createContextMenu(text);
      text.setMenu(menu);
    }
  }

  private void contributeActions() {
    toggleShowComments =
        new Action(
            Policy.bind("HistoryView.showComments"), // $NON-NLS-1$
            SVNUIPlugin.getPlugin().getImageDescriptor(ISVNUIConstants.IMG_COMMENTS)) {
          public void run() {
            showComments = isChecked();
            setViewerVisibility();
            store.setValue(ISVNUIConstants.PREF_SHOW_COMMENTS, showComments);
          }
        };
    toggleShowComments.setChecked(showComments);
    // PlatformUI.getWorkbench().getHelpSystem().setHelp(toggleTextAction,
    // IHelpContextIds.SHOW_COMMENT_IN_HISTORY_ACTION);

    // Toggle wrap comments action
    toggleWrapCommentsAction =
        new Action(Policy.bind("HistoryView.wrapComments")) { // $NON-NLS-1$
          public void run() {
            wrapCommentsText = isChecked();
            setViewerVisibility();
            store.setValue(ISVNUIConstants.PREF_WRAP_COMMENTS, wrapCommentsText);
          }
        };
    toggleWrapCommentsAction.setChecked(wrapCommentsText);
    // PlatformUI.getWorkbench().getHelpSystem().setHelp(toggleTextWrapAction,
    // IHelpContextIds.SHOW_TAGS_IN_HISTORY_ACTION);

    // Toggle path visible action
    toggleShowAffectedPathsAction =
        new Action(
            Policy.bind("HistoryView.showAffectedPaths"), // $NON-NLS-1$
            SVNUIPlugin.getPlugin()
                .getImageDescriptor(ISVNUIConstants.IMG_AFFECTED_PATHS_FLAT_MODE)) {
          public void run() {
            showAffectedPaths = isChecked();
            setViewerVisibility();
            store.setValue(ISVNUIConstants.PREF_SHOW_PATHS, showAffectedPaths);
          }
        };
    toggleShowAffectedPathsAction.setChecked(showAffectedPaths);
    // PlatformUI.getWorkbench().getHelpSystem().setHelp(toggleListAction,
    // IHelpContextIds.SHOW_TAGS_IN_HISTORY_ACTION);

    // Toggle stop on copy action
    toggleStopOnCopyAction =
        new Action(Policy.bind("HistoryView.stopOnCopy")) { // $NON-NLS-1$
          public void run() {
            refresh();
            SVNUIPlugin.getPlugin()
                .getPreferenceStore()
                .setValue(ISVNUIConstants.PREF_STOP_ON_COPY, toggleStopOnCopyAction.isChecked());
          }
        };
    toggleStopOnCopyAction.setChecked(store.getBoolean(ISVNUIConstants.PREF_STOP_ON_COPY));

    // Toggle include merged revisions action
    toggleIncludeMergedRevisionsAction =
        new Action(Policy.bind("HistoryView.includeMergedRevisions")) { // $NON-NLS-1$
          public void run() {
            store.setValue(
                ISVNUIConstants.PREF_INCLUDE_MERGED_REVISIONS,
                toggleIncludeMergedRevisionsAction.isChecked());
            refreshTable();
            refresh();
          }
        };
    toggleIncludeMergedRevisionsAction.setChecked(
        store.getBoolean(ISVNUIConstants.PREF_INCLUDE_MERGED_REVISIONS));

    // Toggle compare to previous action
    toggleCompareToPreviousAction =
        new Action(Policy.bind("HistoryView.compareToPrevious")) { // $NON-NLS-1$
          public void run() {
            store.setValue(
                ISVNUIConstants.PREF_COMPARE_TO_PREVIOUS,
                toggleCompareToPreviousAction.isChecked());
          }
        };
    toggleCompareToPreviousAction.setChecked(
        store.getBoolean(ISVNUIConstants.PREF_COMPARE_TO_PREVIOUS));

    IHistoryPageSite parentSite = getHistoryPageSite();
    IPageSite pageSite = parentSite.getWorkbenchPageSite();
    IActionBars actionBars = pageSite.getActionBars();

    // Contribute toggle text visible to the toolbar drop-down
    IMenuManager actionBarsMenu = actionBars.getMenuManager();
    actionBarsMenu.add(getGetNextAction());
    actionBarsMenu.add(getGetAllAction());
    actionBarsMenu.add(toggleStopOnCopyAction);
    actionBarsMenu.add(toggleIncludeMergedRevisionsAction);
    actionBarsMenu.add(toggleCompareToPreviousAction);
    actionBarsMenu.add(new Separator());
    actionBarsMenu.add(toggleWrapCommentsAction);
    actionBarsMenu.add(new Separator());
    actionBarsMenu.add(toggleShowComments);
    actionBarsMenu.add(toggleShowAffectedPathsAction);
    actionBarsMenu.add(new Separator());
    for (int i = 0; i < toggleAffectedPathsModeActions.length; i++) {
      actionBarsMenu.add(toggleAffectedPathsModeActions[i]);
    }
    actionBarsMenu.add(new Separator());
    for (int i = 0; i < toggleAffectedPathsLayoutActions.length; i++) {
      actionBarsMenu.add(toggleAffectedPathsLayoutActions[i]);
    }

    // Create the local tool bar
    IToolBarManager tbm = actionBars.getToolBarManager();
    // tbm.add(getRefreshAction());
    tbm.add(new Separator());
    tbm.add(getSearchAction());
    tbm.add(getClearSearchAction());
    tbm.add(new Separator());
    tbm.add(toggleShowComments);
    tbm.add(toggleShowAffectedPathsAction);
    tbm.add(new Separator());
    tbm.add(getGetNextAction());
    tbm.add(getGetAllAction());
    // tbm.add(getLinkWithEditorAction());
    tbm.update(false);

    actionBars.updateActionBars();
  }

  ILogEntry getLogEntry(IStructuredSelection ss) {
    if (ss.getFirstElement() instanceof LogEntryChangePath) {
      return ((LogEntryChangePath) ss.getFirstElement()).getLogEntry();
    }
    return (ILogEntry) ss.getFirstElement();
  }

  void updatePanels(ISelection selection) {
    if (selection == null || !(selection instanceof IStructuredSelection)) {
      textViewer.setDocument(new Document("")); // $NON-NLS-1$
      changePathsViewer.setInput(null);
      return;
    }
    IStructuredSelection ss = (IStructuredSelection) selection;
    if (ss.size() != 1) {
      textViewer.setDocument(new Document("")); // $NON-NLS-1$
      changePathsViewer.setInput(null);
      return;
    }
    LogEntry entry = (LogEntry) ss.getFirstElement();
    textViewer.setDocument(new Document(entry.getComment()));
    StyledText text = textViewer.getTextWidget();

    // TODO move this logic into the hyperlink detector created in createText()
    if (projectProperties == null) {
      linkList = ProjectProperties.getUrls(entry.getComment());
    } else {
      linkList = projectProperties.getLinkList(entry.getComment());
    }
    if (linkList != null) {
      int[][] linkRanges = linkList.getLinkRanges();
      // String[] urls = linkList.getUrls();
      for (int i = 0; i < linkRanges.length; i++) {
        text.setStyleRange(
            new StyleRange(
                linkRanges[i][0],
                linkRanges[i][1],
                JFaceColors.getHyperlinkText(Display.getCurrent()),
                null));
      }
    }
    if (changePathsViewer instanceof ChangePathsTreeViewer) {
      ((ChangePathsTreeViewer) changePathsViewer).setCurrentLogEntry(entry);
    }
    if (changePathsViewer instanceof ChangePathsFlatViewer) {
      ((ChangePathsFlatViewer) changePathsViewer).setCurrentLogEntry(entry);
    }
    if (changePathsViewer instanceof ChangePathsTableProvider) {
      ((ChangePathsTableProvider) changePathsViewer).setCurrentLogEntry(entry);
    }
    changePathsViewer.setInput(entry);
  }

  void setViewerVisibility() {
    if (showComments && showAffectedPaths) {
      svnHistoryPageControl.setMaximizedControl(null);
      innerSashForm.setMaximizedControl(null);
    } else if (showComments) {
      svnHistoryPageControl.setMaximizedControl(null);
      innerSashForm.setMaximizedControl(textViewer.getTextWidget());
    } else if (showAffectedPaths) {
      svnHistoryPageControl.setMaximizedControl(null);
      innerSashForm.setMaximizedControl(changePathsViewer.getControl());
    } else {
      svnHistoryPageControl.setMaximizedControl(tableParent);
    }
    changePathsViewer.refresh();
    textViewer.getTextWidget().setWordWrap(wrapCommentsText);
  }

  void setCurrentLogEntryChangePath(final LogEntryChangePath[] currentLogEntryChangePath) {
    this.currentLogEntryChangePath = currentLogEntryChangePath;
    if (!shutdown) {
      // Getting the changePaths
      /*
       * final SVNRevision.Number revisionId =
       * remoteResource.getLastChangedRevision();
       */
      getSite()
          .getShell()
          .getDisplay()
          .asyncExec(
              new Runnable() {
                public void run() {
                  if (currentLogEntryChangePath != null
                      && changePathsViewer != null
                      && !changePathsViewer.getControl().isDisposed()) {
                    // once we got the changePath, we refresh the table
                    changePathsViewer.refresh();
                    // selectRevision(revisionId);
                  }
                }
              });
    }
  }

  /** Select the revision in the receiver. */
  public void selectRevision(SVNRevision.Number revision) {
    if (entries == null) {
      return;
    }

    ILogEntry entry = null;
    for (int i = 0; i < entries.length; i++) {
      if (entries[i].getRevision().equals(revision)) {
        entry = entries[i];
        break;
      }
    }

    if (entry != null) {
      IStructuredSelection selection = new StructuredSelection(entry);
      tableHistoryViewer.setSelection(selection, true);
    }
  }

  public void scheduleFetchChangePathJob(ILogEntry logEntry) {
    if (fetchChangePathJob == null) {
      fetchChangePathJob = new FetchChangePathJob();
    }
    if (fetchChangePathJob.getState() != Job.NONE) {
      fetchChangePathJob.cancel();
      try {
        fetchChangePathJob.join();
      } catch (InterruptedException e) {
        SVNUIPlugin.log(IStatus.ERROR, e.getMessage(), e);
        // SVNUIPlugin.log(new
        // SVNException(Policy.bind("HistoryView.errorFetchingEntries",
        // remoteResource.getName()), e)); //$NON-NLS-1$
      }
    }
    fetchChangePathJob.setLogEntry(logEntry);
    Utils.schedule(fetchChangePathJob, getSite());
  }

  public boolean isShowChangePaths() {
    return showAffectedPaths;
  }

  private SVNRevision getSelectedRevision() {
    IStructuredSelection sel = (IStructuredSelection) tableHistoryViewer.getSelection();
    if (sel.getFirstElement() instanceof ILogEntry) {
      return ((ILogEntry) sel.getFirstElement()).getRevision();
    }
    return null;
  }

  private IAction getOpenRemoteFileAction() {
    if (openAction == null) {
      openAction =
          new Action() {
            public void run() {
              OpenRemoteFileAction delegate = new OpenRemoteFileAction();
              delegate.init(this);
              delegate.selectionChanged(this, tableHistoryViewer.getSelection());
              if (isEnabled()) {
                try {
                  // disableEditorActivation = true;
                  delegate.run(this);
                } finally {
                  // disableEditorActivation = false;
                }
              }
            }
          };
    }
    return openAction;
  }

  private IAction getCompareWithPreviousAction() {
    if (compareWithPreviousAction == null) {
      compareWithPreviousAction =
          new Action() {
            public void run() {
              CompareRemoteResourcesAction delegate = new CompareRemoteResourcesAction();
              delegate.init(this);
              ILogEntry logEntry =
                  (ILogEntry)
                      ((IStructuredSelection) tableHistoryViewer.getSelection()).getFirstElement();
              int from = Integer.parseInt(logEntry.getRevision().toString());
              from--;
              String to = Integer.toString(from);
              SVNRevision toRevision = null;
              try {
                toRevision = SVNRevision.getRevision(to);
              } catch (ParseException e) {
              }
              ISVNRemoteResource[] remoteResources = new ISVNRemoteResource[2];
              ISVNRemoteResource remoteResource1;
              ISVNRemoteResource remoteResource2;
              if (logEntry.getResource().getResource() instanceof IContainer) {
                remoteResource1 =
                    new RemoteFolder(
                        null,
                        logEntry.getResource().getRepository(),
                        logEntry.getResource().getUrl(),
                        logEntry.getRevision(),
                        logEntry.getRevision(),
                        null,
                        null);
                remoteResource2 =
                    new RemoteFolder(
                        null,
                        logEntry.getResource().getRepository(),
                        logEntry.getResource().getUrl(),
                        (SVNRevision.Number) toRevision,
                        (SVNRevision.Number) toRevision,
                        null,
                        null);
              } else {
                remoteResource1 =
                    new RemoteFile(
                        null,
                        logEntry.getResource().getRepository(),
                        logEntry.getResource().getUrl(),
                        logEntry.getRevision(),
                        logEntry.getRevision(),
                        null,
                        null);
                remoteResource2 =
                    new RemoteFile(
                        null,
                        logEntry.getResource().getRepository(),
                        logEntry.getResource().getUrl(),
                        (SVNRevision.Number) toRevision,
                        (SVNRevision.Number) toRevision,
                        null,
                        null);
              }
              remoteResources[0] = remoteResource1;
              remoteResources[1] = remoteResource2;
              delegate.selectionChanged(this, new StructuredSelection(remoteResources));
              delegate.setRemoteResources(remoteResources);
              delegate.setLocalResource(getResource());
              delegate.setLocalResources(remoteResources);
              delegate.run(this);
            }
          };
    }
    return compareWithPreviousAction;
  }

  private IAction getCompareWithPreviousChangedPathAction() {
    if (compareWithPreviousChangedPathAction == null) {
      compareWithPreviousChangedPathAction =
          new Action() {
            public void run() {
              CompareRemoteResourcesAction delegate = new CompareRemoteResourcesAction();
              delegate.init(this);
              ISVNRemoteResource remoteResource1 = null;
              Object firstSelection =
                  ((IStructuredSelection) changePathsViewer.getSelection()).getFirstElement();
              if (firstSelection instanceof LogEntryChangePath) {
                LogEntryChangePath logEntryChangePath = (LogEntryChangePath) firstSelection;
                try {
                  remoteResource1 = logEntryChangePath.getRemoteResource();
                } catch (SVNException e1) {
                  SVNUIPlugin.openError(
                      getSite().getShell(), null, null, e1, SVNUIPlugin.LOG_TEAM_EXCEPTIONS);
                  return;
                }
              } else if (firstSelection instanceof HistoryFolder) {
                HistoryFolder historyFolder = (HistoryFolder) firstSelection;
                Object[] children = historyFolder.getChildren();
                if (children != null
                    && children.length > 0
                    && children[0] instanceof LogEntryChangePath) {
                  LogEntryChangePath logEntryChangePath = (LogEntryChangePath) children[0];
                  try {
                    SVNUrl svnUrl =
                        logEntryChangePath
                            .getRemoteResource()
                            .getRepository()
                            .getRemoteFolder(historyFolder.getPath())
                            .getUrl();
                    SVNRevision.Number selectedRevision =
                        (SVNRevision.Number) getSelectedRevision();
                    remoteResource1 =
                        new RemoteFolder(
                            null,
                            logEntryChangePath.getLogEntry().getRemoteResource().getRepository(),
                            svnUrl,
                            selectedRevision,
                            selectedRevision,
                            null,
                            null);
                  } catch (Exception e) {
                  }
                }
              }
              if (remoteResource1 == null) {
                return;
              }
              int from = Integer.parseInt(remoteResource1.getRevision().toString());
              from--;
              String to = Integer.toString(from);
              SVNRevision toRevision = null;
              try {
                toRevision = SVNRevision.getRevision(to);
              } catch (ParseException e) {
              }
              ISVNRemoteResource[] remoteResources = new ISVNRemoteResource[2];
              ISVNRemoteResource remoteResource2;
              if (firstSelection instanceof HistoryFolder
                  || remoteResource1.getResource() instanceof IContainer) {
                remoteResource2 =
                    new RemoteFolder(
                        null,
                        remoteResource1.getRepository(),
                        remoteResource1.getUrl(),
                        (SVNRevision.Number) toRevision,
                        (SVNRevision.Number) toRevision,
                        null,
                        null);
              } else {
                remoteResource2 =
                    new RemoteFile(
                        null,
                        remoteResource1.getRepository(),
                        remoteResource1.getUrl(),
                        (SVNRevision.Number) toRevision,
                        (SVNRevision.Number) toRevision,
                        null,
                        null);
              }
              remoteResources[0] = remoteResource1;
              remoteResources[1] = remoteResource2;
              delegate.selectionChanged(this, new StructuredSelection(remoteResources));
              delegate.setRemoteResources(remoteResources);
              delegate.setLocalResource(remoteResource1.getResource());
              delegate.setLocalResources(remoteResources);
              SVNRevision[] pegRevisions = {remoteResource1.getRevision()};
              delegate.setPegRevisions(pegRevisions);
              delegate.run(this);
            }
          };
    }
    return compareWithPreviousChangedPathAction;
  }

  private boolean isFile() {
    IStructuredSelection sel = (IStructuredSelection) changePathsViewer.getSelection();
    if (sel.size() == 1 && sel.getFirstElement() instanceof LogEntryChangePath) {
      LogEntryChangePath changePath = (LogEntryChangePath) sel.getFirstElement();
      try {
        return changePath.getRemoteResource() instanceof ISVNRemoteFile;
      } catch (SVNException e) {
      }
    }
    return false;
  }

  // open changed Path (double-click)
  private IAction getOpenChangedPathAction() {
    if (openChangedPathAction == null) {
      openChangedPathAction =
          new Action("Open") { // $NON-NLS-1$
            public void run() {
              if (!isFile()) {
                MessageDialog.openError(
                    Display.getDefault().getActiveShell(),
                    Policy.bind("SVNHistoryPage.7"),
                    Policy.bind("SVNHistoryPage.8")); // $NON-NLS-1$ //$NON-NLS-2$
                return;
              }
              OpenRemoteFileAction delegate = new OpenRemoteFileAction();
              delegate.setUsePegRevision(true);
              delegate.init(this);
              delegate.selectionChanged(this, changePathsViewer.getSelection());
              if (isEnabled()) {
                try {
                  delegate.run(this);
                } finally {
                  // disableEditorActivation = false;
                }
              }
            }
          };
    }
    //  openChangedPathAction.setEnabled(isFile());
    return openChangedPathAction;
  }

  private IAction getShowHistoryAction() {
    if (showHistoryAction == null) {
      showHistoryAction =
          new Action(
              "Show History",
              SVNUIPlugin.getPlugin()
                  .getImageDescriptor(ISVNUIConstants.IMG_MENU_SHOWHISTORY)) { // $NON-NLS-1$
            public void run() {
              HistoryAction delegate = new HistoryAction();
              delegate.selectionChanged(this, changePathsViewer.getSelection());
              delegate.run(this);
            }
          };
    }
    return showHistoryAction;
  }

  private IAction getCopyChangedPathAction() {
    if (copyChangedPathAction == null) {
      copyChangedPathAction =
          new Action(Policy.bind("HistoryView.copyChangedPath")) { // $NON-NLS-1$
            public void run() {
              ContainerSelectionDialog dialog =
                  new ContainerSelectionDialog(
                      Display.getDefault().getActiveShell(),
                      null,
                      false,
                      Policy.bind("CopyAction.selectionLabel")); // $NON-NLS-1$
              if (dialog.open() == ContainerSelectionDialog.OK) {
                Object[] result = dialog.getResult();
                if (result == null || result.length == 0) return;
                final Path path = (Path) result[0];
                IProject selectedProject;
                File target = null;
                if (path.segmentCount() == 1) {
                  selectedProject =
                      ResourcesPlugin.getWorkspace().getRoot().getProject(path.toString());
                  target = selectedProject.getLocation().toFile();
                } else {
                  IFile targetFile = ResourcesPlugin.getWorkspace().getRoot().getFile(path);
                  selectedProject = targetFile.getProject();
                  target = targetFile.getLocation().toFile();
                }
                final IProject targetProject = selectedProject;
                final File destPath = target;
                BusyIndicator.showWhile(
                    Display.getCurrent(),
                    new Runnable() {
                      public void run() {
                        ISVNClientAdapter client = null;
                        try {
                          IStructuredSelection sel =
                              (IStructuredSelection) changePathsViewer.getSelection();
                          if (sel.getFirstElement() instanceof LogEntryChangePath) {
                            LogEntryChangePath changePath =
                                (LogEntryChangePath) sel.getFirstElement();
                            SVNRevision revision = changePath.getRevision();
                            if (changePath.getAction() == 'D') {
                              long rev = Long.parseLong(revision.toString());
                              rev--;
                              revision = new SVNRevision.Number(rev);
                            }
                            client = SVNProviderPlugin.getPlugin().getSVNClient();
                            client.copy(
                                changePath.getUrl(), destPath, revision, revision, true, false);
                            targetProject.refreshLocal(
                                IResource.DEPTH_INFINITE, new NullProgressMonitor());
                            SVNUIPlugin.getPlugin()
                                .getRepositoryManager()
                                .resourceCreated(null, null);
                          }
                        } catch (Exception e) {
                          MessageDialog.openError(
                              Display.getDefault().getActiveShell(),
                              Policy.bind("HistoryView.copyError"),
                              e.getMessage()); // $NON-NLS-1$
                        } finally {
                          SVNProviderPlugin.getPlugin()
                              .getSVNClientManager()
                              .returnSVNClient(client);
                        }
                      }
                    });
              }
            }
          };
    }
    return copyChangedPathAction;
  }

  private IAction getExportAction() {
    if (exportAction == null) {
      exportAction =
          new Action(
              "Export...",
              SVNUIPlugin.getPlugin()
                  .getImageDescriptor(ISVNUIConstants.IMG_MENU_EXPORT)) { // $NON-NLS-1$
            public void run() {
              ExportAction delegate = new ExportAction();
              delegate.selectionChanged(this, changePathsViewer.getSelection());
              delegate.run(this);
            }
          };
    }
    return exportAction;
  }

  private IAction getShowAnnotationAction() {
    if (showAnnotationAction == null) {
      showAnnotationAction =
          new Action(
              "Show Annotation",
              SVNUIPlugin.getPlugin()
                  .getImageDescriptor(ISVNUIConstants.IMG_MENU_ANNOTATE)) { // $NON-NLS-1$
            public void run() {
              if (!isFile()) {
                MessageDialog.openError(
                    Display.getDefault().getActiveShell(),
                    Policy.bind("SVNHistoryPage.11"),
                    Policy.bind("SVNHistoryPage.12")); // $NON-NLS-1$ //$NON-NLS-2$
                return;
              }
              AnnotationAction delegate = new AnnotationAction();
              delegate.selectionChanged(this, changePathsViewer.getSelection());
              delegate.run(this);
            }
          };
    }
    //	showAnnotationAction.setEnabled(isFile());
    return showAnnotationAction;
  }

  private IAction getCompareAction() {
    if (compareAction == null) {
      compareAction =
          new Action("Compare...") { // $NON-NLS-1$
            public void run() {
              CompareAction delegate = new CompareAction();
              delegate.selectionChanged(this, changePathsViewer.getSelection());
              delegate.run(this);
            }
          };
    }
    return compareAction;
  }

  private IAction getCreateTagFromRevisionChangedPathAction() {
    if (createTagFromRevisionChangedPathAction == null) {
      createTagFromRevisionChangedPathAction =
          new Action() { //$NON-NLS-1$
            public void run() {
              SVNRevision selectedRevision = null;
              ISelection selection = changePathsViewer.getSelection();
              if (!(selection instanceof IStructuredSelection)) return;
              IStructuredSelection sel = (IStructuredSelection) selection;
              ISVNRemoteResource remoteResource = null;
              if (sel.getFirstElement() instanceof LogEntryChangePath) {
                try {
                  remoteResource = ((LogEntryChangePath) sel.getFirstElement()).getRemoteResource();
                  selectedRevision = remoteResource.getRevision();
                } catch (SVNException e) {
                }
              } else if (sel.getFirstElement() instanceof HistoryFolder) {
                HistoryFolder historyFolder = (HistoryFolder) sel.getFirstElement();
                Object[] children = historyFolder.getChildren();
                if (children != null
                    && children.length > 0
                    && children[0] instanceof LogEntryChangePath) {
                  LogEntryChangePath changePath = (LogEntryChangePath) children[0];
                  try {
                    remoteResource =
                        changePath
                            .getRemoteResource()
                            .getRepository()
                            .getRemoteFolder(historyFolder.getPath());
                    selectedRevision = getSelectedRevision();
                  } catch (SVNException e) {
                  }
                }
              }
              if (remoteResource == null) return;
              ISVNRemoteResource[] remoteResources = {remoteResource};
              BranchTagWizard wizard = new BranchTagWizard(remoteResources);
              wizard.setRevisionNumber(Long.parseLong(selectedRevision.toString()));
              WizardDialog dialog = new ClosableWizardDialog(getSite().getShell(), wizard);
              if (dialog.open() == WizardDialog.OK) {
                final SVNUrl sourceUrl = wizard.getUrl();
                final SVNUrl destinationUrl = wizard.getToUrl();
                final String message = wizard.getComment();
                final SVNRevision revision = wizard.getRevision();
                final boolean makeParents = wizard.isMakeParents();
                try {
                  BusyIndicator.showWhile(
                      Display.getCurrent(),
                      new Runnable() {
                        public void run() {
                          ISVNClientAdapter client = null;
                          try {
                            client =
                                SVNProviderPlugin.getPlugin().getSVNClientManager().getSVNClient();
                            client.copy(sourceUrl, destinationUrl, message, revision, makeParents);
                            SVNUIPlugin.getPlugin()
                                .getRepositoryManager()
                                .resourceCreated(null, null);
                          } catch (Exception e) {
                            MessageDialog.openError(
                                getSite().getShell(),
                                Policy.bind("HistoryView.createTagFromRevision"),
                                e //$NON-NLS-1$
                                    .getMessage());
                          } finally {
                            SVNProviderPlugin.getPlugin()
                                .getSVNClientManager()
                                .returnSVNClient(client);
                          }
                        }
                      });
                } catch (Exception e) {
                  MessageDialog.openError(
                      getSite().getShell(),
                      Policy.bind("HistoryView.createTagFromRevision"),
                      e //$NON-NLS-1$
                          .getMessage());
                }
              }
              //	  		SvnWizardBranchTagPage branchTagPage = new
              // SvnWizardBranchTagPage(remoteResource);
              //
              // branchTagPage.setRevisionNumber(Long.parseLong(selectedRevision.toString()));
              //        	SvnWizard wizard = new SvnWizard(branchTagPage);
              //            SvnWizardDialog dialog = new SvnWizardDialog(getSite().getShell(),
              // wizard);
              //            wizard.setParentDialog(dialog);
              //            if (!(dialog.open() == SvnWizardDialog.OK)) return;
              //            final SVNUrl sourceUrl = branchTagPage.getUrl();
              //            final SVNUrl destinationUrl = branchTagPage.getToUrl();
              //            final String message = branchTagPage.getComment();
              //            final SVNRevision revision = branchTagPage.getRevision();
              //            final boolean makeParents = branchTagPage.isMakeParents();
              //            try {
              //                BusyIndicator.showWhile(Display.getCurrent(), new Runnable() {
              //                    public void run() {
              //                      try {
              //                        ISVNClientAdapter client =
              // SVNProviderPlugin.getPlugin().getSVNClientManager().createSVNClient();
              //                        client.copy(sourceUrl, destinationUrl, message, revision,
              // makeParents);
              //                      } catch(Exception e) {
              //                        MessageDialog.openError(getSite().getShell(),
              // Policy.bind("HistoryView.createTagFromRevision"), e
              //                            .getMessage());
              //                      }
              //                    }
              //                  });
              //            } catch(Exception e) {
              //              MessageDialog.openError(getSite().getShell(),
              // Policy.bind("HistoryView.createTagFromRevision"), e
              //                  .getMessage());
              //            }
            }
          };
    }

    ISelection selection = changePathsViewer.getSelection();
    if (selection instanceof IStructuredSelection) {
      IStructuredSelection sel = (IStructuredSelection) selection;
      SVNRevision selectedRevision = null;
      if (sel.size() == 1) {
        //        ISVNRemoteResource remoteResource = null;
        if (sel.getFirstElement() instanceof LogEntryChangePath
            && ((LogEntryChangePath) sel.getFirstElement()).getAction() != 'D') {
          //			  try {
          //				remoteResource = ((LogEntryChangePath)sel.getFirstElement()).getRemoteResource();
          selectedRevision = ((LogEntryChangePath) sel.getFirstElement()).getRevision();
          //				selectedRevision = remoteResource.getRevision();
          //			} catch (SVNException e) {}
        } else if (sel.getFirstElement() instanceof HistoryFolder) {
          HistoryFolder historyFolder = (HistoryFolder) sel.getFirstElement();
          Object[] children = historyFolder.getChildren();
          if (children != null
              && children.length > 0
              && children[0] instanceof LogEntryChangePath) {
            selectedRevision = getSelectedRevision();
          }
        }
        createTagFromRevisionChangedPathAction.setEnabled(selectedRevision != null);
        if (selectedRevision == null) {
          createTagFromRevisionChangedPathAction.setText(
              Policy.bind(
                  "HistoryView.createTagFromRevision",
                  "" //$NON-NLS-1$ //$NON-NLS-2$
                      + ((LogEntryChangePath) sel.getFirstElement()).getRevision()));
        } else {
          createTagFromRevisionChangedPathAction.setText(
              Policy.bind(
                  "HistoryView.createTagFromRevision",
                  "" //$NON-NLS-1$ //$NON-NLS-2$
                      + selectedRevision));
        }
      }
    }
    createTagFromRevisionChangedPathAction.setImageDescriptor(
        SVNUIPlugin.getPlugin().getImageDescriptor(ISVNUIConstants.IMG_MENU_BRANCHTAG));

    return createTagFromRevisionChangedPathAction;
  }

  class AnnotationAction extends ShowAnnotationAction {
    public IStructuredSelection fSelection;

    public AnnotationAction() {
      super();
    }

    protected ISVNRemoteFile getSingleSelectedSVNRemoteFile() {
      ISVNRemoteResource remoteResource = null;
      if (fSelection.getFirstElement() instanceof LogEntryChangePath) {
        try {
          remoteResource = ((LogEntryChangePath) fSelection.getFirstElement()).getRemoteResource();
          if (remoteResource instanceof RemoteResource) {
            ((RemoteResource) remoteResource)
                .setPegRevision(((LogEntryChangePath) fSelection.getFirstElement()).getRevision());
          }
        } catch (SVNException e) {
        }
      }
      return (ISVNRemoteFile) remoteResource;
    }

    protected boolean isEnabled() {
      return true;
    }

    public void selectionChanged(IAction action, ISelection sel) {
      if (sel instanceof IStructuredSelection) {
        fSelection = (IStructuredSelection) sel;
      }
    }
  }

  class HistoryAction extends ShowHistoryAction {
    public IStructuredSelection fSelection;

    public HistoryAction() {
      super();
    }

    protected ISVNRemoteResource[] getSelectedRemoteResources() {
      ISVNRemoteResource remoteResource = null;
      if (fSelection.getFirstElement() instanceof LogEntryChangePath) {
        try {
          remoteResource = ((LogEntryChangePath) fSelection.getFirstElement()).getRemoteResource();
        } catch (SVNException e) {
        }
      } else if (fSelection.getFirstElement() instanceof HistoryFolder) {
        HistoryFolder historyFolder = (HistoryFolder) fSelection.getFirstElement();
        Object[] children = historyFolder.getChildren();
        if (children != null && children.length > 0 && children[0] instanceof LogEntryChangePath) {
          LogEntryChangePath changePath = (LogEntryChangePath) children[0];
          try {
            remoteResource =
                changePath
                    .getRemoteResource()
                    .getRepository()
                    .getRemoteFolder(historyFolder.getPath());
          } catch (SVNException e) {
          }
        }
      }
      if (remoteResource != null) {
        ISVNRemoteResource[] selectedResource = {remoteResource};
        return selectedResource;
      }
      return new ISVNRemoteResource[0];
    }

    protected boolean isEnabled() {
      return true;
    }

    public void selectionChanged(IAction action, ISelection sel) {
      if (sel instanceof IStructuredSelection) {
        fSelection = (IStructuredSelection) sel;
      }
    }
  }

  class ExportAction extends ExportRemoteFolderAction {
    public IStructuredSelection fSelection;

    public ExportAction() {
      super();
    }

    protected ISVNRemoteResource[] getSelectedRemoteResources() {
      ISVNRemoteResource remoteResource = null;
      if (fSelection.getFirstElement() instanceof LogEntryChangePath) {
        try {
          remoteResource = ((LogEntryChangePath) fSelection.getFirstElement()).getRemoteResource();
        } catch (SVNException e) {
        }
      }
      if (remoteResource != null) {
        ISVNRemoteResource[] selectedResource = {remoteResource};
        return selectedResource;
      }
      return new ISVNRemoteResource[0];
    }

    protected boolean isEnabled() {
      return true;
    }

    public void selectionChanged(IAction action, ISelection sel) {
      if (sel instanceof IStructuredSelection) {
        fSelection = (IStructuredSelection) sel;
      }
    }
  }

  class CompareAction extends ShowDifferencesAsUnifiedDiffAction {
    public IStructuredSelection fSelection;;

    public CompareAction() {
      super();
      setUsePegRevision(true);
    }

    protected ISVNRemoteResource[] getSelectedRemoteResources() {
      ISVNRemoteResource remoteResource = null;
      if (fSelection.getFirstElement() instanceof LogEntryChangePath) {
        try {
          remoteResource = ((LogEntryChangePath) fSelection.getFirstElement()).getRemoteResource();
          if (remoteResource instanceof RemoteResource) {
            ((RemoteResource) remoteResource)
                .setPegRevision(((LogEntryChangePath) fSelection.getFirstElement()).getRevision());
          }
        } catch (SVNException e) {
        }
      } else if (fSelection.getFirstElement() instanceof HistoryFolder) {
        HistoryFolder historyFolder = (HistoryFolder) fSelection.getFirstElement();
        Object[] children = historyFolder.getChildren();
        if (children != null && children.length > 0 && children[0] instanceof LogEntryChangePath) {
          LogEntryChangePath changePath = (LogEntryChangePath) children[0];
          try {
            ISVNRemoteResource changePathResource = changePath.getRemoteResource();
            ISVNRemoteResource remoteFolder =
                changePathResource.getRepository().getRemoteFolder(historyFolder.getPath());
            remoteResource =
                new RemoteFolder(
                    null,
                    changePathResource.getRepository(),
                    remoteFolder.getUrl(),
                    changePathResource.getRevision(),
                    (SVNRevision.Number) changePathResource.getRevision(),
                    null,
                    null);
          } catch (SVNException e) {
          }
        }
      }
      if (remoteResource != null) {
        ISVNRemoteResource[] selectedResource = {remoteResource};
        return selectedResource;
      }
      return new ISVNRemoteResource[0];
    }

    protected boolean isEnabled() {
      return true;
    }

    public void selectionChanged(IAction action, ISelection sel) {
      if (sel instanceof IStructuredSelection) {
        fSelection = (IStructuredSelection) sel;
      }
    }
  }

  // get contents Action (context menu)
  private IAction getGetContentsAction() {
    if (getContentsAction == null) {
      getContentsAction =
          getContextMenuAction(
              Policy.bind("HistoryView.getContentsAction"),
              new IWorkspaceRunnable() { //$NON-NLS-1$
                public void run(IProgressMonitor monitor) throws CoreException {
                  ISelection selection = getSelection();
                  if (!(selection instanceof IStructuredSelection)) return;
                  IStructuredSelection ss = (IStructuredSelection) selection;
                  ISVNRemoteFile remoteFile = (ISVNRemoteFile) getLogEntry(ss).getRemoteResource();
                  monitor.beginTask(null, 100);
                  try {
                    if (remoteFile != null) {
                      if (confirmOverwrite()) {
                        if (remoteFile instanceof RemoteResource) {
                          if (resource != null) {
                            ISVNLocalResource localResource =
                                SVNWorkspaceRoot.getSVNResourceFor(resource);
                            ((RemoteResource) remoteFile)
                                .setPegRevision(localResource.getRevision());
                          } else {
                            ((RemoteResource) remoteFile).setPegRevision(SVNRevision.HEAD);
                          }
                        }
                        InputStream in =
                            ((IResourceVariant) remoteFile)
                                .getStorage(new SubProgressMonitor(monitor, 50))
                                .getContents();
                        IFile file = (IFile) resource;
                        file.setContents(in, false, true, new SubProgressMonitor(monitor, 50));
                      }
                    }
                  } catch (TeamException e) {
                    throw new CoreException(e.getStatus());
                  } finally {
                    monitor.done();
                  }
                }
              });
      PlatformUI.getWorkbench()
          .getHelpSystem()
          .setHelp(getContentsAction, IHelpContextIds.GET_FILE_CONTENTS_ACTION);
    }
    return getContentsAction;
  }

  // update to the selected revision (context menu)
  private IAction getUpdateToRevisionAction() {
    if (updateToRevisionAction == null) {
      updateToRevisionAction =
          getContextMenuAction(
              Policy.bind("HistoryView.getRevisionAction"),
              new IWorkspaceRunnable() { //$NON-NLS-1$
                public void run(IProgressMonitor monitor) throws CoreException {
                  ISelection selection = getSelection();
                  if (!(selection instanceof IStructuredSelection)) return;
                  IStructuredSelection ss = (IStructuredSelection) selection;
                  ISVNRemoteFile remoteFile = (ISVNRemoteFile) getLogEntry(ss).getRemoteResource();
                  try {
                    if (remoteFile != null) {
                      if (confirmOverwrite()) {
                        IFile file = (IFile) resource;
                        new ReplaceOperation(
                                getSite().getPage().getActivePart(),
                                file,
                                remoteFile.getLastChangedRevision())
                            .run(monitor);
                        historyTableProvider.setRemoteResource(remoteFile);
                        historyTableProvider.setProjectProperties(
                            ProjectProperties.getProjectProperties(resource));
                        Display.getDefault()
                            .asyncExec(
                                new Runnable() {
                                  public void run() {
                                    tableHistoryViewer.refresh();
                                  }
                                });
                      }
                    }
                  } catch (InvocationTargetException e) {
                    throw new CoreException(new SVNStatus(IStatus.ERROR, 0, e.getMessage()));
                  } catch (InterruptedException e) {
                    // Cancelled by user
                  }
                }
              });
      PlatformUI.getWorkbench()
          .getHelpSystem()
          .setHelp(updateToRevisionAction, IHelpContextIds.GET_FILE_REVISION_ACTION);
    }
    return updateToRevisionAction;
  }

  // get switch action (context menu)
  private IAction getSwitchAction() {
    if (switchAction == null) {
      switchAction =
          new Action() {
            public void run() {
              if (selection instanceof IStructuredSelection) {
                IStructuredSelection ss = (IStructuredSelection) selection;
                if (ss.size() == 1) {
                  ILogEntry currentSelection = getLogEntry(ss);
                  IResource[] resources = {resource};
                  SvnWizardSwitchPage switchPage =
                      new SvnWizardSwitchPage(
                          resources, currentSelection.getRevision().getNumber());
                  SvnWizard wizard = new SvnWizard(switchPage);
                  SvnWizardDialog dialog = new SvnWizardDialog(getSite().getShell(), wizard);
                  wizard.setParentDialog(dialog);
                  if (dialog.open() == SvnWizardDialog.OK) {
                    SVNUrl[] svnUrls = switchPage.getUrls();
                    SVNRevision svnRevision = switchPage.getRevision();
                    SwitchOperation switchOperation =
                        new SwitchOperation(
                            getSite().getPage().getActivePart(), resources, svnUrls, svnRevision);
                    switchOperation.setDepth(switchPage.getDepth());
                    switchOperation.setSetDepth(switchPage.isSetDepth());
                    switchOperation.setIgnoreExternals(switchPage.isIgnoreExternals());
                    switchOperation.setForce(switchPage.isForce());
                    switchOperation.setIgnoreAncestry(switchPage.isIgnoreAncestry());
                    switchOperation.setConflictResolver(switchPage.getConflictResolver());
                    try {
                      switchOperation.run();
                    } catch (Exception e) {
                      MessageDialog.openError(
                          getSite().getShell(), switchAction.getText(), e.getMessage());
                    }
                  }
                }
              }
            }
          };
    }
    ISelection selection = getSelection();
    if (selection instanceof IStructuredSelection) {
      IStructuredSelection ss = (IStructuredSelection) selection;
      if (ss.size() == 1) {
        ILogEntry currentSelection = getLogEntry(ss);
        switchAction.setText(
            Policy.bind(
                "HistoryView.switchToRevision",
                "" //$NON-NLS-1$ //$NON-NLS-2$
                    + currentSelection.getRevision().getNumber()));
      }
    }
    switchAction.setImageDescriptor(
        SVNUIPlugin.getPlugin().getImageDescriptor(ISVNUIConstants.IMG_MENU_SWITCH));
    return switchAction;
  }

  // get create tag from revision action (context menu)
  private IAction getCreateTagFromRevisionAction() {
    if (createTagFromRevisionAction == null) {
      createTagFromRevisionAction =
          new Action() {
            public void run() {
              ISelection selection = getSelection();
              if (!(selection instanceof IStructuredSelection)) return;
              ILogEntry currentSelection = getLogEntry((IStructuredSelection) selection);
              BranchTagWizard wizard;
              if (resource == null) {
                ISVNRemoteResource[] remoteResources = {historyTableProvider.getRemoteResource()};
                wizard = new BranchTagWizard(remoteResources);
              } else {
                IResource[] resources = {resource};
                wizard = new BranchTagWizard(resources);
              }
              wizard.setRevisionNumber(currentSelection.getRevision().getNumber());
              WizardDialog dialog = new ClosableWizardDialog(getSite().getShell(), wizard);
              if (dialog.open() == WizardDialog.OK) {
                final SVNUrl sourceUrl = wizard.getUrl();
                final SVNUrl destinationUrl = wizard.getToUrl();
                final String message = wizard.getComment();
                final SVNRevision revision = wizard.getRevision();
                final boolean makeParents = wizard.isMakeParents();
                boolean createOnServer = wizard.isCreateOnServer();
                IResource[] resources = {resource};
                try {
                  if (resource == null) {
                    BusyIndicator.showWhile(
                        Display.getCurrent(),
                        new Runnable() {
                          public void run() {
                            ISVNClientAdapter client = null;
                            try {
                              client =
                                  SVNProviderPlugin.getPlugin()
                                      .getSVNClientManager()
                                      .getSVNClient();
                              client.copy(
                                  sourceUrl, destinationUrl, message, revision, makeParents);
                              SVNUIPlugin.getPlugin()
                                  .getRepositoryManager()
                                  .resourceCreated(null, null);
                            } catch (Exception e) {
                              MessageDialog.openError(
                                  getSite().getShell(),
                                  Policy.bind("HistoryView.createTagFromRevision"),
                                  e //$NON-NLS-1$
                                      .getMessage());
                            } finally {
                              SVNProviderPlugin.getPlugin()
                                  .getSVNClientManager()
                                  .returnSVNClient(client);
                            }
                          }
                        });
                  } else {
                    BranchTagOperation branchTagOperation =
                        new BranchTagOperation(
                            getSite().getPage().getActivePart(),
                            resources,
                            new SVNUrl[] {sourceUrl},
                            destinationUrl,
                            createOnServer,
                            wizard.getRevision(),
                            message);
                    branchTagOperation.setMakeParents(makeParents);
                    branchTagOperation.setNewAlias(wizard.getNewAlias());
                    branchTagOperation.run();
                  }
                } catch (Exception e) {
                  MessageDialog.openError(
                      getSite().getShell(),
                      Policy.bind("HistoryView.createTagFromRevision"),
                      e //$NON-NLS-1$
                          .getMessage());
                }
              }
            }
          };
    }
    ISelection selection = getSelection();
    if (selection instanceof IStructuredSelection) {
      IStructuredSelection ss = (IStructuredSelection) selection;
      if (ss.size() == 1) {
        ILogEntry currentSelection = getLogEntry(ss);
        createTagFromRevisionAction.setText(
            Policy.bind(
                "HistoryView.createTagFromRevision",
                "" //$NON-NLS-1$ //$NON-NLS-2$
                    + currentSelection.getRevision().getNumber()));
      }
    }
    createTagFromRevisionAction.setImageDescriptor(
        SVNUIPlugin.getPlugin().getImageDescriptor(ISVNUIConstants.IMG_MENU_BRANCHTAG));
    return createTagFromRevisionAction;
  }

  private IAction getSetCommitPropertiesAction() {
    // set Action (context menu)
    if (setCommitPropertiesAction == null) {
      setCommitPropertiesAction =
          new Action(Policy.bind("HistoryView.setCommitProperties")) { // $NON-NLS-1$
            public void run() {
              try {
                final ISelection selection = getSelection();
                if (!(selection instanceof IStructuredSelection)) return;
                final ILogEntry ourSelection = getLogEntry((IStructuredSelection) selection);

                // Failing that, try the resource originally selected by the user if
                // from the Team menu

                // TODO: Search all paths from currentSelection and find the
                // shortest path and
                // get the resources for that instance (in order to get the 'best'
                // "bugtraq" properties)
                final ProjectProperties projectProperties =
                    (resource != null)
                        ? ProjectProperties.getProjectProperties(resource)
                        : (ourSelection.getRemoteResource() != null)
                            ? ProjectProperties.getProjectProperties(
                                ourSelection.getRemoteResource())
                            : ProjectProperties.getProjectProperties(
                                remoteResource); // will return null!

                final ISVNResource svnResource =
                    ourSelection.getRemoteResource() != null
                        ? ourSelection.getRemoteResource()
                        : ourSelection.getResource();

                SetCommitPropertiesDialog dialog =
                    new SetCommitPropertiesDialog(
                        getSite().getShell(),
                        ourSelection.getRevision(),
                        resource,
                        projectProperties);
                // Set previous text - the text to edit
                dialog.setOldAuthor(ourSelection.getAuthor());
                dialog.setOldComment(ourSelection.getComment());

                boolean doCommit = (dialog.open() == Window.OK);
                if (doCommit) {
                  final String author;
                  final String commitComment;
                  if (ourSelection.getAuthor().equals(dialog.getAuthor())) author = null;
                  else author = dialog.getAuthor();
                  if (ourSelection.getComment().equals(dialog.getComment())) commitComment = null;
                  else commitComment = dialog.getComment();

                  final ChangeCommitPropertiesCommand command =
                      new ChangeCommitPropertiesCommand(
                          svnResource.getRepository(),
                          ourSelection.getRevision(),
                          commitComment,
                          author);

                  PlatformUI.getWorkbench()
                      .getProgressService()
                      .run(
                          true,
                          true,
                          new IRunnableWithProgress() {
                            public void run(IProgressMonitor monitor)
                                throws InvocationTargetException {
                              try {
                                command.run(monitor);
                              } catch (SVNException e) {
                                throw new InvocationTargetException(e);
                              } finally {
                                if (ourSelection instanceof LogEntry) {
                                  LogEntry logEntry = (LogEntry) ourSelection;
                                  if (command.isLogMessageChanged())
                                    logEntry.setComment(commitComment);
                                  if (command.isAuthorChanged()) logEntry.setAuthor(author);
                                }
                                getSite()
                                    .getShell()
                                    .getDisplay()
                                    .asyncExec(
                                        new Runnable() {
                                          public void run() {
                                            tableHistoryViewer.refresh();
                                            tableHistoryViewer.setSelection(selection, true);
                                          }
                                        });
                              }
                            }
                          });
                }
              } catch (InvocationTargetException e) {
                SVNUIPlugin.openError(
                    getSite().getShell(), null, null, e, SVNUIPlugin.LOG_NONTEAM_EXCEPTIONS);
              } catch (InterruptedException e) {
                // Do nothing
              } catch (SVNException e) {
                // TODO Auto-generated catch block
                SVNUIPlugin.openError(
                    getSite().getShell(), null, null, e, SVNUIPlugin.LOG_TEAM_EXCEPTIONS);
              }
            }

            // we don't allow multiple selection
            public boolean isEnabled() {
              ISelection selection = getSelection();
              return selection instanceof IStructuredSelection
                  && ((IStructuredSelection) selection).size() == 1;
            }
          };
    }
    return setCommitPropertiesAction;
  }

  private IAction getShowRevisionPropertiesAction() {
    if (showRevisionPropertiesAction == null) {
      showRevisionPropertiesAction =
          new Action(Policy.bind("HistoryView.showRevisionProperties")) { // $NON-NLS-1$
            public void run() {
              try {
                final ISelection selection = getSelection();
                if (!(selection instanceof IStructuredSelection)) return;
                final ILogEntry ourSelection = getLogEntry((IStructuredSelection) selection);

                ISVNRemoteResource selectionRemoteResource = ourSelection.getRemoteResource();
                SvnRevPropertiesView revPropsView = null;

                try {
                  revPropsView =
                      (SvnRevPropertiesView)
                          getSite().getPage().showView(SvnRevPropertiesView.VIEW_ID);
                } catch (PartInitException e) {
                  SVNUIPlugin.openError(
                      getSite().getShell(), null, null, e, SVNUIPlugin.LOG_TEAM_EXCEPTIONS);
                }

                if (revPropsView != null) {
                  revPropsView.showSvnProperties(selectionRemoteResource);
                  revPropsView.refresh();
                }
              } catch (SVNException e) {
                SVNUIPlugin.openError(
                    getSite().getShell(), null, null, e, SVNUIPlugin.LOG_TEAM_EXCEPTIONS);
              }
            }

            // we don't allow multiple selection
            public boolean isEnabled() {
              ISelection selection = getSelection();
              return selection instanceof IStructuredSelection
                  && ((IStructuredSelection) selection).size() == 1;
            }
          };
    }
    return showRevisionPropertiesAction;
  }

  private IAction getShowRevisionsAction() {
    if (showRevisionsAction == null) {
      showRevisionsAction =
          new Action(Policy.bind("HistoryView.showMergedRevisions")) { // $NON-NLS-1$
            public void run() {
              ISelection selection = getSelection();
              if (!(selection instanceof IStructuredSelection)) return;
              IStructuredSelection ss = (IStructuredSelection) selection;
              ILogEntry logEntry = (ILogEntry) ss.getFirstElement();
              ShowRevisionsDialog dialog = null;
              if (resource != null)
                dialog =
                    new ShowRevisionsDialog(
                        getSite().getShell(), logEntry, resource, includeTags, SVNHistoryPage.this);
              else if (remoteResource != null)
                dialog =
                    new ShowRevisionsDialog(
                        getSite().getShell(),
                        logEntry,
                        remoteResource,
                        includeTags,
                        SVNHistoryPage.this);
              if (dialog != null) dialog.open();
            }
          };
    }
    return showRevisionsAction;
  }

  private SVNRevisionRange[] getRevisionRanges() {
    List revisionRanges = new ArrayList();
    ISelection selection = getSelection();
    if (selection instanceof IStructuredSelection) {
      IStructuredSelection ss = (IStructuredSelection) selection;
      List selectionList = ss.toList();
      TableItem[] items = tableHistoryViewer.getTable().getItems();
      SVNRevision revision1 = null;
      SVNRevision revision2 = null;
      for (int i = 0; i < items.length; i++) {
        if (items[i].getData() instanceof ILogEntry) {
          ILogEntry logEntry = (ILogEntry) items[i].getData();
          if (selectionList.contains(logEntry)) {
            if (revision1 == null) {
              revision1 = logEntry.getRevision();
            }
            revision2 = logEntry.getRevision();
            revertEnabled = true;
            LogEntryChangePath[] changePaths = logEntry.getLogEntryChangePaths();
            if (changePaths != null) {
              for (LogEntryChangePath changePath : changePaths) {
                if (changePath.getPath().equals(remoteResource.getRepositoryRelativePath())) {
                  if (changePath.getAction() == 'A') {
                    revertEnabled = false;
                  }
                  break;
                }
              }
            }
          } else {
            if (revision1 != null) {
              SVNRevisionRange revisionRange = new SVNRevisionRange(revision1, revision2);
              revisionRanges.add(revisionRange);
              revision1 = null;
              revision2 = null;
            }
          }
        }
      }
      if (revision1 != null) {
        SVNRevisionRange revisionRange = new SVNRevisionRange(revision1, revision2);
        revisionRanges.add(revisionRange);
      }
    }
    SVNRevisionRange[] revisionRangeArray = new SVNRevisionRange[revisionRanges.size()];
    revisionRanges.toArray(revisionRangeArray);
    return revisionRangeArray;
  }

  // get revert changes action (context menu)
  private IAction getRevertChangesAction() {
    revisionRanges = getRevisionRanges();
    if (revertChangesAction == null) {
      revertChangesAction =
          new Action() {
            public void run() {
              ISelection selection = getSelection();
              if (!(selection instanceof IStructuredSelection)) return;
              final IStructuredSelection ss = (IStructuredSelection) selection;
              if (ss.size() == 1) {
                if (!MessageDialog.openConfirm(
                    getSite().getShell(),
                    Policy.bind("HistoryView.revertRevision"),
                    Policy.bind(
                        "HistoryView.confirmRevertRevision",
                        resource.getFullPath().toString()))) // $NON-NLS-1$
                return;
              } else {
                if (!MessageDialog.openConfirm(
                    getSite().getShell(),
                    Policy.bind("HistoryView.revertRevisions"),
                    Policy.bind(
                        "HistoryView.confirmRevertRevisions",
                        resource.getFullPath().toString()))) // $NON-NLS-1$
                return;
              }
              BusyIndicator.showWhile(
                  Display.getCurrent(),
                  new Runnable() {
                    public void run() {
                      ILogEntry firstElement = getFirstElement();
                      ILogEntry lastElement = getLastElement();
                      final IResource[] resources = {resource};
                      try {
                        final SVNUrl path1 =
                            new SVNUrl(firstElement.getResource().getUrl() + "@HEAD");
                        final SVNUrl path2 =
                            new SVNUrl(lastElement.getResource().getUrl() + "@HEAD");
                        for (int i = 0; i < revisionRanges.length; i++) {
                          final SVNRevision revision1 = revisionRanges[i].getFromRevision();
                          final SVNRevision revision2 =
                              new SVNRevision.Number(
                                  ((SVNRevision.Number) revisionRanges[i].getToRevision())
                                          .getNumber()
                                      - 1);
                          WorkspaceAction mergeAction =
                              new WorkspaceAction() {
                                protected void execute(IAction action)
                                    throws InvocationTargetException, InterruptedException {
                                  new MergeOperation(
                                          getSite().getPage().getActivePart(),
                                          resources,
                                          path1,
                                          revision1,
                                          path2,
                                          revision2)
                                      .run();
                                }
                              };
                          mergeAction.run(null);
                        }
                      } catch (Exception e) {
                        MessageDialog.openError(
                            getSite().getShell(), revertChangesAction.getText(), e.getMessage());
                      }
                    }
                  });
            }
          };
    }
    ISelection selection = getSelection();
    if (selection instanceof IStructuredSelection) {
      IStructuredSelection ss = (IStructuredSelection) selection;
      if (ss.size() == 1) {
        ILogEntry currentSelection = getLogEntry(ss);
        revertChangesAction.setText(
            Policy.bind(
                "HistoryView.revertChangesFromRevision",
                "" //$NON-NLS-1$ //$NON-NLS-2$
                    + currentSelection.getRevision().getNumber()));
      }
      if (ss.size() > 1) {
        revertChangesAction.setText(
            Policy.bind("HistoryView.revertChangesFromRevisions")); // $NON-NLS-1$
      }
    }
    revertChangesAction.setImageDescriptor(
        SVNUIPlugin.getPlugin().getImageDescriptor(ISVNUIConstants.IMG_MENU_MARKMERGED));
    revertChangesAction.setEnabled(revertEnabled);
    return revertChangesAction;
  }

  private GenerateChangeLogAction getGenerateChangeLogAction() {
    if (generateChangeLogAction == null)
      generateChangeLogAction =
          new GenerateChangeLogAction(
              new ISelectionProvider() {
                public void addSelectionChangedListener(ISelectionChangedListener listener) {}

                public ISelection getSelection() {
                  return SVNHistoryPage.this.getSelection();
                }

                public void removeSelectionChangedListener(ISelectionChangedListener listener) {}

                public void setSelection(ISelection selection) {}
              });
    return generateChangeLogAction;
  }

  // Refresh action (toolbar)
  private IAction getRefreshAction() {
    if (refreshAction == null) {
      SVNUIPlugin plugin = SVNUIPlugin.getPlugin();
      refreshAction =
          new Action(
              Policy.bind("HistoryView.refreshLabel"),
              plugin.getImageDescriptor(ISVNUIConstants.IMG_REFRESH_ENABLED)) { // $NON-NLS-1$
            public void run() {
              refresh();
            }
          };
      refreshAction.setToolTipText(Policy.bind("HistoryView.refresh")); // $NON-NLS-1$
      refreshAction.setDisabledImageDescriptor(
          plugin.getImageDescriptor(ISVNUIConstants.IMG_REFRESH_DISABLED));
      refreshAction.setHoverImageDescriptor(plugin.getImageDescriptor(ISVNUIConstants.IMG_REFRESH));
    }
    return refreshAction;
  }

  // Search action (toolbar)
  private IAction getSearchAction() {
    if (searchAction == null) {
      SVNUIPlugin plugin = SVNUIPlugin.getPlugin();
      searchAction =
          new Action(
              Policy.bind("HistoryView.search"),
              plugin.getImageDescriptor(ISVNUIConstants.IMG_FILTER_HISTORY)) { // $NON-NLS-1$
            public void run() {
              if (historySearchDialog == null) {
                historySearchDialog = new HistorySearchDialog(getSite().getShell(), remoteResource);
              }
              historySearchDialog.setRemoteResource(remoteResource);
              if (historySearchDialog.open() == Window.OK) {
                searchAction.setEnabled(false);
                Utils.schedule(
                    new SearchHistoryJob(),
                    SVNUIPlugin.getPlugin()
                        .getWorkbench()
                        .getActiveWorkbenchWindow()
                        .getActivePage()
                        .getActivePart()
                        .getSite());
              }
            }
          };
      searchAction.setDisabledImageDescriptor(
          plugin.getImageDescriptor(ISVNUIConstants.IMG_FILTER_HISTORY_DISABLED));
    }
    return searchAction;
  }

  // Clear search action (toolbar)
  private IAction getClearSearchAction() {
    if (clearSearchAction == null) {
      SVNUIPlugin plugin = SVNUIPlugin.getPlugin();
      clearSearchAction =
          new Action(
              Policy.bind("HistoryView.clearSearch"),
              plugin.getImageDescriptor(ISVNUIConstants.IMG_CLEAR)) { // $NON-NLS-1$
            public void run() {
              BusyIndicator.showWhile(
                  tableHistoryViewer.getTable().getDisplay(),
                  new Runnable() {
                    public void run() {
                      ViewerFilter[] filters = tableHistoryViewer.getFilters();
                      for (int i = 0; i < filters.length; i++) {
                        ViewerFilter filter = filters[i];
                        if (filter instanceof HistorySearchViewerFilter) {
                          tableHistoryViewer.removeFilter(filter);
                        } else if (filter instanceof EmptySearchViewerFilter) {
                          tableHistoryViewer.removeFilter(filter);
                        }
                      }
                      setEnabled(false);
                      if (!historySearchDialog.getSearchAllLogs()
                          && (historySearchDialog.getStartRevision() != null
                              || historySearchDialog.getEndRevision() != null)) {
                        getRefreshAction().run();
                      }
                    }
                  });
            }
          };
      clearSearchAction.setDisabledImageDescriptor(
          plugin.getImageDescriptor(ISVNUIConstants.IMG_CLEAR_DISABLED));
    }
    return clearSearchAction;
  }

  // Get Get All action (toolbar)
  private IAction getGetAllAction() {
    if (getAllAction == null) {
      SVNUIPlugin plugin = SVNUIPlugin.getPlugin();
      getAllAction =
          new Action(
              Policy.bind("HistoryView.getAll"),
              plugin.getImageDescriptor(ISVNUIConstants.IMG_GET_ALL)) { // $NON-NLS-1$
            public void run() {
              final ISVNRemoteResource remoteResource = historyTableProvider.getRemoteResource();
              if (fetchAllLogEntriesJob == null) {
                fetchAllLogEntriesJob = new FetchAllLogEntriesJob();
              }
              if (fetchAllLogEntriesJob.getState() != Job.NONE) {
                fetchAllLogEntriesJob.cancel();
                try {
                  fetchAllLogEntriesJob.join();
                } catch (InterruptedException e) {
                  SVNUIPlugin.log(
                      new SVNException(
                          Policy.bind("HistoryView.errorFetchingEntries", remoteResource.getName()),
                          e)); //$NON-NLS-1$
                }
              }
              fetchAllLogEntriesJob.setRemoteFile(remoteResource);
              Utils.schedule(fetchAllLogEntriesJob, getSite());
            }
          };
      getAllAction.setToolTipText(Policy.bind("HistoryView.getAll")); // $NON-NLS-1$
    }
    return getAllAction;
  }

  // Get Get Next action (toolbar)
  public IAction getGetNextAction() {
    if (getNextAction == null) {
      getNextAction = new GetNextAction();
    }
    return getNextAction;
  }

  /**
   * All context menu actions use this class This action : - updates currentSelection - action.run
   */
  private Action getContextMenuAction(String title, final IWorkspaceRunnable action) {
    return new Action(title) {
      public void run() {
        try {
          if (resource == null) return;
          PlatformUI.getWorkbench()
              .getProgressService()
              .run(
                  true,
                  true,
                  new IRunnableWithProgress() {
                    public void run(IProgressMonitor monitor) throws InvocationTargetException {
                      try {
                        action.run(monitor);
                      } catch (CoreException e) {
                        throw new InvocationTargetException(e);
                      }
                    }
                  });
        } catch (InvocationTargetException e) {
          SVNUIPlugin.openError(
              getSite().getShell(), null, null, e, SVNUIPlugin.LOG_NONTEAM_EXCEPTIONS);
        } catch (InterruptedException e) {
          // Do nothing
        }
      }

      // we don't allow multiple selection
      public boolean isEnabled() {
        ISelection selection = getSelection();
        return selection instanceof IStructuredSelection
            && ((IStructuredSelection) selection).size() == 1;
      }
    };
  }

  /**
   * Ask the user to confirm the overwrite of the file if the file has been modified since last
   * commit
   */
  private boolean confirmOverwrite() {
    IFile file = (IFile) resource;
    if (file != null && file.exists()) {
      ISVNLocalFile svnFile = SVNWorkspaceRoot.getSVNFileFor(file);
      try {
        if (svnFile.isDirty()) {
          String title = Policy.bind("HistoryView.overwriteTitle"); // $NON-NLS-1$
          String msg = Policy.bind("HistoryView.overwriteMsg"); // $NON-NLS-1$
          final MessageDialog dialog =
              new MessageDialog(
                  getSite().getShell(),
                  title,
                  null,
                  msg,
                  MessageDialog.QUESTION,
                  new String[] {IDialogConstants.YES_LABEL, IDialogConstants.CANCEL_LABEL},
                  0);
          final int[] result = new int[1];
          getSite()
              .getShell()
              .getDisplay()
              .syncExec(
                  new Runnable() {
                    public void run() {
                      result[0] = dialog.open();
                    }
                  });
          if (result[0] != 0) {
            // cancel
            return false;
          }
        }
      } catch (SVNException e) {
        SVNUIPlugin.log(e.getStatus());
      }
    }
    return true;
  }

  private ISelection getSelection() {
    return selection;
  }

  private ILogEntry getFirstElement() {
    ILogEntry firstElement = null;
    ISelection selection = getSelection();
    if (selection instanceof IStructuredSelection) {
      IStructuredSelection ss = (IStructuredSelection) selection;
      Iterator iter = ss.iterator();
      while (iter.hasNext()) {
        ILogEntry element = (ILogEntry) iter.next();
        if (firstElement == null
            || element.getRevision().getNumber() > firstElement.getRevision().getNumber())
          firstElement = element;
      }
    }
    return firstElement;
  }

  private ILogEntry getLastElement() {
    ILogEntry lastElement = null;
    ISelection selection = getSelection();
    if (selection instanceof IStructuredSelection) {
      IStructuredSelection ss = (IStructuredSelection) selection;
      Iterator iter = ss.iterator();
      while (iter.hasNext()) {
        ILogEntry element = (ILogEntry) iter.next();
        if (lastElement == null
            || element.getRevision().getNumber() < lastElement.getRevision().getNumber())
          lastElement = element;
      }
    }
    return lastElement;
  }

  private final class GetNextAction extends Action implements IPropertyChangeListener {
    GetNextAction() {
      super(
          Policy.bind("HistoryView.getNext"),
          SVNUIPlugin.getPlugin().getImageDescriptor(ISVNUIConstants.IMG_GET_NEXT)); // $NON-NLS-1$
      updateFromProperties();
      SVNUIPlugin.getPlugin().getPreferenceStore().addPropertyChangeListener(this);
    }

    public void run() {
      final ISVNRemoteResource remoteResource = historyTableProvider.getRemoteResource();
      if (fetchNextLogEntriesJob == null) {
        fetchNextLogEntriesJob = new FetchNextLogEntriesJob();
      }
      if (fetchNextLogEntriesJob.getState() != Job.NONE) {
        fetchNextLogEntriesJob.cancel();
        try {
          fetchNextLogEntriesJob.join();
        } catch (InterruptedException e) {
          SVNUIPlugin.log(
              new SVNException(
                  Policy.bind("HistoryView.errorFetchingEntries", remoteResource.getName()),
                  e)); //$NON-NLS-1$
        }
      }
      fetchNextLogEntriesJob.setRemoteFile(remoteResource);
      Utils.schedule(fetchNextLogEntriesJob, getSite());
    }

    public void propertyChange(PropertyChangeEvent event) {
      if (ISVNUIConstants.PREF_LOG_ENTRIES_TO_FETCH.equals(event.getProperty())) {
        updateFromProperties();
      }
    }

    private void updateFromProperties() {
      int entriesToFetch =
          SVNUIPlugin.getPlugin()
              .getPreferenceStore()
              .getInt(ISVNUIConstants.PREF_LOG_ENTRIES_TO_FETCH);
      setToolTipText(
          Policy.bind("HistoryView.getNext") + " " + entriesToFetch); // $NON-NLS-1$ //$NON-NLS-2$
      if (entriesToFetch <= 0) {
        setEnabled(false);
      }
    }
  }

  private class FetchLogEntriesJob extends AbstractFetchJob {
    public ISVNRemoteResource remoteResource;

    public FetchLogEntriesJob() {
      super(Policy.bind("HistoryView.fetchHistoryJob")); // $NON-NLS-1$;
    }

    public void setRemoteFile(ISVNRemoteResource resource) {
      this.remoteResource = resource;
    }

    public IStatus run(IProgressMonitor monitor) {
      try {
        if (remoteResource != null && !shutdown) {
          if (resource == null) {
            if (remoteResource == null
                || !SVNUIPlugin.getPlugin()
                    .getPreferenceStore()
                    .getBoolean(ISVNUIConstants.PREF_SHOW_TAGS_IN_REMOTE)) {
              tagManager = null;
            } else {
              tagManager = new AliasManager(remoteResource.getUrl());
            }
          } else {
            tagManager = new AliasManager(resource);
          }
          final SVNRevision pegRevision = remoteResource.getRevision();
          SVNRevision revisionEnd = new SVNRevision.Number(0);
          boolean stopOnCopy = toggleStopOnCopyAction.isChecked();
          boolean includeMergedRevisions = toggleIncludeMergedRevisionsAction.isChecked();
          int entriesToFetch = store.getInt(ISVNUIConstants.PREF_LOG_ENTRIES_TO_FETCH);
          long limit = entriesToFetch;
          try {
            entries =
                getLogEntries(
                    monitor,
                    remoteResource,
                    pegRevision,
                    revisionStart,
                    revisionEnd,
                    stopOnCopy,
                    limit + 1,
                    tagManager,
                    includeMergedRevisions);
          } catch (TeamException e) {
            if (revisionStart.equals(SVNRevision.HEAD)
                && pegRevision != null
                && e.getMessage() != null
                && e.getMessage().contains("svn: Unable to find repository location")) {
              revisionStart = pegRevision;
              entries =
                  getLogEntries(
                      monitor,
                      remoteResource,
                      pegRevision,
                      revisionStart,
                      revisionEnd,
                      stopOnCopy,
                      limit + 1,
                      tagManager,
                      includeMergedRevisions);
            } else {
              throw e;
            }
          }

          long entriesLength = entries.length;
          if (entriesLength > limit) {
            ILogEntry[] fetchedEntries = new ILogEntry[entries.length - 1];
            for (int i = 0; i < entries.length - 1; i++) {
              fetchedEntries[i] = entries[i];
            }
            entries = fetchedEntries;
            getNextAction.setEnabled(true);
          } else {
            getNextAction.setEnabled(false);
          }
          final SVNRevision.Number revisionId = remoteResource.getLastChangedRevision();
          getSite()
              .getShell()
              .getDisplay()
              .asyncExec(
                  new Runnable() {
                    public void run() {
                      if (entries != null
                          && tableHistoryViewer != null
                          && !tableHistoryViewer.getTable().isDisposed()) {
                        // once we got the entries, we refresh the table
                        if (entries.length > 0) {
                          lastEntry = entries[entries.length - 1];
                          long lastEntryNumber = lastEntry.getRevision().getNumber();
                          revisionStart = new SVNRevision.Number(lastEntryNumber - 1);
                        }
                        tableHistoryViewer.refresh();
                        selectRevision(revisionId);
                      }
                    }
                  });
        }
        return Status.OK_STATUS;
      } catch (TeamException e) {
        if (e instanceof SVNException) {
          if (((SVNException) e).operationInterrupted()) {
            return Status.OK_STATUS;
          }
        }
        return e.getStatus();
      }
    }
  }

  private class FetchNextLogEntriesJob extends AbstractFetchJob {
    public ISVNRemoteResource remoteResource;

    public FetchNextLogEntriesJob() {
      super(Policy.bind("HistoryView.fetchHistoryJob")); // $NON-NLS-1$;
    }

    public void setRemoteFile(ISVNRemoteResource resource) {
      this.remoteResource = resource;
    }

    public IStatus run(IProgressMonitor monitor) {
      try {
        if (remoteResource != null && !shutdown) {
          SVNRevision pegRevision = remoteResource.getRevision();
          SVNRevision revisionEnd = new SVNRevision.Number(0);
          boolean stopOnCopy = toggleStopOnCopyAction.isChecked();
          boolean includeMergedRevisions = toggleIncludeMergedRevisionsAction.isChecked();
          int entriesToFetch = store.getInt(ISVNUIConstants.PREF_LOG_ENTRIES_TO_FETCH);
          long limit = entriesToFetch;
          ILogEntry[] nextEntries =
              getLogEntries(
                  monitor,
                  remoteResource,
                  pegRevision,
                  revisionStart,
                  revisionEnd,
                  stopOnCopy,
                  limit + 1,
                  tagManager,
                  includeMergedRevisions);
          long entriesLength = nextEntries.length;
          ILogEntry[] fetchedEntries = null;
          if (entriesLength > limit) {
            fetchedEntries = new ILogEntry[nextEntries.length - 1];
            for (int i = 0; i < nextEntries.length - 1; i++) fetchedEntries[i] = nextEntries[i];
            getNextAction.setEnabled(true);
          } else {
            fetchedEntries = new ILogEntry[nextEntries.length];
            for (int i = 0; i < nextEntries.length; i++) fetchedEntries[i] = nextEntries[i];
            getNextAction.setEnabled(false);
          }
          ArrayList entryArray = new ArrayList();
          if (entries == null) entries = new ILogEntry[0];
          for (int i = 0; i < entries.length; i++) entryArray.add(entries[i]);
          for (int i = 0; i < fetchedEntries.length; i++) entryArray.add(fetchedEntries[i]);
          entries = new ILogEntry[entryArray.size()];
          entryArray.toArray(entries);
          getSite()
              .getShell()
              .getDisplay()
              .asyncExec(
                  new Runnable() {
                    public void run() {
                      if (entries != null
                          && tableHistoryViewer != null
                          && !tableHistoryViewer.getTable().isDisposed()) {
                        // once we got the entries, we refresh the table
                        ISelection selection = tableHistoryViewer.getSelection();
                        tableHistoryViewer.refresh();
                        tableHistoryViewer.setSelection(selection);
                      }
                    }
                  });
          if (entries.length > 0) {
            lastEntry = entries[entries.length - 1];
            long lastEntryNumber = lastEntry.getRevision().getNumber();
            revisionStart = new SVNRevision.Number(lastEntryNumber - 1);
          }
        }
        return Status.OK_STATUS;
      } catch (TeamException e) {
        return e.getStatus();
      }
    }
  }

  private abstract class AbstractFetchJob extends Job {
    public AbstractFetchJob(String name) {
      super(name);
    }

    public abstract void setRemoteFile(ISVNRemoteResource resource);

    protected ILogEntry[] getLogEntries(
        IProgressMonitor monitor,
        ISVNRemoteResource remoteResource,
        SVNRevision pegRevision,
        SVNRevision revisionStart,
        SVNRevision revisionEnd,
        boolean stopOnCopy,
        long limit,
        AliasManager tagManager,
        boolean includeMergedRevisions)
        throws TeamException {
      // If filtering by revision range, pass upper/lower revisions to API and override limit.
      SVNRevision start = revisionStart;
      SVNRevision end = revisionEnd;
      long fetchLimit = limit;
      if (historySearchDialog != null && !historySearchDialog.getSearchAllLogs()) {
        if (historySearchDialog.getStartRevision() != null
            || historySearchDialog.getEndRevision() != null) {
          if (getClearSearchAction().isEnabled()) {
            if (historySearchDialog.getStartRevision() != null)
              end = historySearchDialog.getStartRevision();
            if (historySearchDialog.getEndRevision() != null)
              start = historySearchDialog.getEndRevision();
            fetchLimit = 0;
            getGetNextAction().setEnabled(false);
          }
        }
      }
      ISVNClientAdapter svnClient = remoteResource.getRepository().getSVNClient();
      try {
        CancelableSVNLogMessageCallback callback =
            new CancelableSVNLogMessageCallback(monitor, svnClient);
        GetLogsCommand logCmd =
            new GetLogsCommand(
                remoteResource,
                pegRevision,
                start,
                end,
                stopOnCopy,
                fetchLimit,
                tagManager,
                includeMergedRevisions);
        logCmd.setCallback(callback);
        logCmd.run(monitor);
        return logCmd.getLogEntries();
      } finally {
        remoteResource.getRepository().returnSVNClient(svnClient);
      }
    }
  }

  private class FetchAllLogEntriesJob extends AbstractFetchJob {
    public ISVNRemoteResource remoteResource;

    public FetchAllLogEntriesJob() {
      super(Policy.bind("HistoryView.fetchHistoryJob")); // $NON-NLS-1$;
    }

    public void setRemoteFile(ISVNRemoteResource resource) {
      this.remoteResource = resource;
    }

    public IStatus run(IProgressMonitor monitor) {
      try {
        if (remoteResource != null && !shutdown) {
          if (resource == null) {
            if (remoteResource == null
                || !SVNUIPlugin.getPlugin()
                    .getPreferenceStore()
                    .getBoolean(ISVNUIConstants.PREF_SHOW_TAGS_IN_REMOTE)) tagManager = null;
            else tagManager = new AliasManager(remoteResource.getUrl());
          } else tagManager = new AliasManager(resource);
          SVNRevision pegRevision = remoteResource.getRevision();
          SVNRevision revisionEnd = new SVNRevision.Number(0);
          boolean stopOnCopy = toggleStopOnCopyAction.isChecked();
          boolean includeMergedRevisions = toggleIncludeMergedRevisionsAction.isChecked();
          long limit = 0;
          entries =
              getLogEntries(
                  monitor,
                  remoteResource,
                  pegRevision,
                  SVNRevision.HEAD,
                  revisionEnd,
                  stopOnCopy,
                  limit,
                  tagManager,
                  includeMergedRevisions);
          final SVNRevision.Number revisionId = remoteResource.getLastChangedRevision();
          getSite()
              .getShell()
              .getDisplay()
              .asyncExec(
                  new Runnable() {
                    public void run() {
                      if (entries != null
                          && tableHistoryViewer != null
                          && !tableHistoryViewer.getTable().isDisposed()) {
                        // once we got the entries, we refresh the table
                        if (entries.length > 0) {
                          lastEntry = entries[entries.length - 1];
                          long lastEntryNumber = lastEntry.getRevision().getNumber();
                          revisionStart = new SVNRevision.Number(lastEntryNumber - 1);
                        }
                        tableHistoryViewer.refresh();
                        selectRevision(revisionId);
                      }
                    }
                  });
        }
        return Status.OK_STATUS;
      } catch (TeamException e) {
        return e.getStatus();
      }
    }
  }

  private class SearchHistoryJob extends Job {

    public SearchHistoryJob() {
      super(""); // $NON-NLS-1$
    }

    public IStatus run(IProgressMonitor monitor) {
      if (!historySearchDialog.getSearchAllLogs()
          && (historySearchDialog.getStartRevision() != null
              || historySearchDialog.getEndRevision() != null)) {
        final ISVNRemoteResource remoteResource = historyTableProvider.getRemoteResource();
        if (fetchAllLogEntriesJob == null) {
          fetchAllLogEntriesJob = new FetchAllLogEntriesJob();
        }
        if (fetchAllLogEntriesJob.getState() != Job.NONE) {
          fetchAllLogEntriesJob.cancel();
        }
        fetchAllLogEntriesJob.setRemoteFile(remoteResource);
        Utils.schedule(fetchAllLogEntriesJob, getSite());
      } else {
        Date startDate = historySearchDialog.getStartDate();
        setEmptyViewerFilter();

        // Fetch log entries until start date
        if (historySearchDialog.getAutoFetchLogs()) {
          if (!historySearchDialog.getSearchAllLogs()) {
            Date lastDate = null;
            if (lastEntry != null) {
              lastDate = lastEntry.getDate();
            }
            int numEntries = entries.length;
            int prevNumEntries = -1;
            while ((numEntries != prevNumEntries)
                && ((lastDate == null)
                    || (startDate == null)
                    || (startDate.compareTo(lastDate) <= 0))) {

              if (monitor.isCanceled()) {
                getSearchAction().setEnabled(true);
                removeEmptyViewerFilter();
                return Status.CANCEL_STATUS;
              }

              final ISVNRemoteResource remoteResource = historyTableProvider.getRemoteResource();
              if (fetchNextLogEntriesJob == null) {
                fetchNextLogEntriesJob = new FetchNextLogEntriesJob();
              }
              if (fetchNextLogEntriesJob.getState() != Job.NONE) {
                fetchNextLogEntriesJob.cancel();
              }
              fetchNextLogEntriesJob.setRemoteFile(remoteResource);
              Utils.schedule(fetchNextLogEntriesJob, getSite());
              try {
                fetchNextLogEntriesJob.join();
              } catch (InterruptedException e) {
                SVNUIPlugin.log(
                    new SVNException(
                        Policy.bind("HistoryView.errorFetchingEntries", remoteResource.getName()),
                        e)); //$NON-NLS-1$
              }

              if (entries.length == 0) {
                break;
              }
              lastDate = lastEntry.getDate();
              prevNumEntries = numEntries;
              numEntries = entries.length;
            }
          } else {
            final ISVNRemoteResource remoteResource = historyTableProvider.getRemoteResource();
            if (fetchAllLogEntriesJob == null) {
              fetchAllLogEntriesJob = new FetchAllLogEntriesJob();
            }
            if (fetchAllLogEntriesJob.getState() != Job.NONE) {
              fetchAllLogEntriesJob.cancel();
            }
            fetchAllLogEntriesJob.setRemoteFile(remoteResource);
            Utils.schedule(fetchAllLogEntriesJob, getSite());
          }
        }
      }

      final HistorySearchViewerFilter viewerFilter =
          new HistorySearchViewerFilter(
              historySearchDialog.getUser(),
              historySearchDialog.getComment(),
              historySearchDialog.getStartDate(),
              historySearchDialog.getEndDate(),
              historySearchDialog.getRegExp(),
              historySearchDialog.getStartRevision(),
              historySearchDialog.getEndRevision());

      getSite()
          .getShell()
          .getDisplay()
          .asyncExec(
              new Runnable() {
                public void run() {
                  BusyIndicator.showWhile(
                      tableHistoryViewer.getTable().getDisplay(),
                      new Runnable() {
                        public void run() {
                          getClearSearchAction().run();
                          tableHistoryViewer.addFilter(viewerFilter);
                          getClearSearchAction().setEnabled(true);
                          getSearchAction().setEnabled(true);
                        }
                      });
                }
              });

      removeEmptyViewerFilter();

      return Status.OK_STATUS;
    }

    private void setEmptyViewerFilter() {
      getSite()
          .getShell()
          .getDisplay()
          .asyncExec(
              new Runnable() {
                public void run() {
                  tableHistoryViewer.addFilter(new EmptySearchViewerFilter());
                }
              });
    }

    private void removeEmptyViewerFilter() {
      getSite()
          .getShell()
          .getDisplay()
          .asyncExec(
              new Runnable() {
                public void run() {
                  ViewerFilter[] filters = tableHistoryViewer.getFilters();
                  for (int i = 0; i < filters.length; i++) {
                    if (filters[i] instanceof EmptySearchViewerFilter) {
                      tableHistoryViewer.removeFilter(filters[i]);
                    }
                  }
                }
              });
    }
  }

  class FetchChangePathJob extends Job {
    public ILogEntry logEntry;

    public FetchChangePathJob() {
      super(Policy.bind("HistoryView.fetchChangePathJob")); // $NON-NLS-1$;
    }

    public void setLogEntry(ILogEntry logEntry) {
      this.logEntry = logEntry;
    }

    public IStatus run(IProgressMonitor monitor) {
      if (logEntry.getResource() != null) {
        setCurrentLogEntryChangePath(logEntry.getLogEntryChangePaths());
      }
      return Status.OK_STATUS;
    }
  }

  public static class ToggleAffectedPathsOptionAction extends Action {
    private final SVNHistoryPage page;
    private final String preferenceName;
    private final int value;

    public ToggleAffectedPathsOptionAction(
        SVNHistoryPage page, String label, String icon, String preferenceName, int value) {
      super(Policy.bind(label), AS_RADIO_BUTTON);
      this.page = page;
      this.preferenceName = preferenceName;
      this.value = value;
      setImageDescriptor(SVNUIPlugin.getPlugin().getImageDescriptor(icon));

      IPreferenceStore store = SVNUIPlugin.getPlugin().getPreferenceStore();
      setChecked(value == store.getInt(preferenceName));
    }

    public int getValue() {
      return this.value;
    }

    public void run() {
      if (isChecked()) {
        SVNUIPlugin.getPlugin().getPreferenceStore().setValue(preferenceName, value);
        page.createAffectedPathsViewer();
      }
    }
  }

  /* (non-Javadoc)
   * @see org.tigris.subversion.subclipse.core.IResourceStateChangeListener#resourceSyncInfoChanged(org.eclipse.core.resources.IResource[])
   */
  public void resourceSyncInfoChanged(IResource[] changedResources) {
    for (int i = 0; i < changedResources.length; i++) {
      IResource changedResource = changedResources[i];
      if (changedResource.equals(resource)) {
        resourceChanged();
      }
    }
  }

  /**
   * This method updates the history table, highlighting the current revison without refetching the
   * log entries to preserve bandwidth. The user has to a manual refresh to get the new log entries.
   */
  private void resourceChanged() {
    getSite()
        .getShell()
        .getDisplay()
        .asyncExec(
            new Runnable() {
              public void run() {
                // Preserve the original starting revision so that when Next is pressed
                // the correct log entries will be fetched.
                SVNRevision holdRevisionStart = revisionStart;
                revisionStart = SVNRevision.HEAD;
                ISVNLocalResource localResource = SVNWorkspaceRoot.getSVNResourceFor(resource);
                try {
                  if (localResource != null && !localResource.getStatus().isAdded()) {
                    ISVNRemoteResource baseResource = localResource.getBaseResource();
                    historyTableProvider.setRemoteResource(baseResource);
                    historyTableProvider.setProjectProperties(null);
                    tableHistoryViewer.refresh();
                  }
                } catch (SVNException e) {
                  if (!e.operationInterrupted()) {
                    SVNUIPlugin.openError(getHistoryPageSite().getShell(), null, null, e);
                  }
                }
                revisionStart = holdRevisionStart;
              }
            });
  }

  /* (non-Javadoc)
   * @see org.eclipse.swt.events.KeyListener#keyPressed(org.eclipse.swt.events.KeyEvent)
   */
  public void keyPressed(KeyEvent e) {
    if (e.keyCode == 'f' && e.stateMask == SWT.CTRL) {
      getSearchAction().run();
    }
  }

  /*
   * (non-Javadoc)
   * @see org.eclipse.swt.events.KeyListener#keyReleased(org.eclipse.swt.events.KeyEvent)
   */
  public void keyReleased(KeyEvent e) {}

  /* (non-Javadoc)
   * @see org.tigris.subversion.subclipse.core.IResourceStateChangeListener#resourceModified(org.eclipse.core.resources.IResource[])
   */
  public void resourceModified(IResource[] changedResources) {}

  /* (non-Javadoc)
   * @see org.tigris.subversion.subclipse.core.IResourceStateChangeListener#projectConfigured(org.eclipse.core.resources.IProject)
   */
  public void projectConfigured(IProject project) {}

  /* (non-Javadoc)
   * @see org.tigris.subversion.subclipse.core.IResourceStateChangeListener#projectDeconfigured(org.eclipse.core.resources.IProject)
   */
  public void projectDeconfigured(IProject project) {}

  public void initialize() {}

  public IResource getResource() {
    return resource;
  }

  public static void setHistorySearchViewerFilter(
      HistorySearchViewerFilter historySearchViewerFilter) {
    SVNHistoryPage.historySearchViewerFilter = historySearchViewerFilter;
  }
}
